3

this is my very first project in swift and yes I've see that there are similar question about this topic already around and I've read almost all but I still can't get my code working.

I've a class that implements the WKScriptMessageHandler protocol to provide a bridge between iOS and the hosted javascript in a WKWebView.

Among the parameters passed from javascript (interpreted as NSDictionary), I have one named _method that contains the swift method name that javascript wants to invoke.

Metod name is extracted successfully on swift side and then I try to call it by using a Selector and here is where I fail:

class AppScriptMessageHandler: NSObject, WKScriptMessageHandler {
    func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
        let dict = message.body as! NSDictionary;
        let method = dict.value(forKey: "_method") as? String ?? "";
        let selector = Selector(method);  // method = "getDocumentsFolder"

        let ret = perform(selector)?.takeUnretainedValue()
        print(ret!)
    }

    func getDocumentsFolder() -> String {
        return "Pippo"
    }
}

All goes fine until I try to do let ret = perform(selector). Here I get the following runtime error:

2018-02-27 15:49:37.279730+0100 wktest[8375:198645] *** Terminating app due to uncaught exception 'NSInvalidArgumentException',
reason: '-[wktest.AppScriptMessageHandler getDocumentsFolder]: unrecognized selector sent to instance 0x604000019ef0'

Where I do wrong? I've also tried appending : to method but I still get same error Thanks in advance for any help!

1 Answer 1

2

Try to change it to @objc func getDocumentsFolder() -> String

Swift language uses Table Dispatch for all class methods. But for performing selector on method, this method should be invoked with Message Dispatch.

That's what @objc keyword is doing - it's marks function as dynamic and it will be invoked with Message Dispatch

You can read more about method dispatches here

UPD

Agree with @Hamish comment, @objc will not make this function method dispatch for Swift, but for perform(_:) method.

Sign up to request clarification or add additional context in comments.

3 Comments

It works! Great, thank you! I have another question: is it correct to use the .takeUnretainedValue() on return value? I want it to be garbage collected after using in. Or do I have to also call the .autorelease() methd?
Ok I have found the sanwer here: stackoverflow.com/questions/29048826/…
@objc actually doesn't have much to do with method dispatch, it merely exposes the method to Obj-C; Swift will still call the method using table or even direct dispatch if possible. The dynamic attribute is what forces Swift to call a method using message dispatch. Though of course, if the method is being invoked through the Obj-C runtime, such as in this case with perform(_:), message dispatch will be used.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.