Search code examples
iosswiftwebviewuiwebviewwkwebview

Getting Javascript function return from Swift WkWebView interface(similar to Android JS interfaces) in IOS 10


I am currently trying to port an app from Android to ios that applies a lot of logic through webviews.

In the android version, I can have something such as

function sendInfo(fileName, dataContent){
    Android.saveData(fileName, dataContent);
}

And deal with the interface through the java portion

@JavascriptInterface
public void saveData(String fileName, String dataContents) {
  //.. do something with the received information
}

I am trying to do the same thing inside ios although I am currently having issues. Mind you we are using Swift 3 in IOS10. What I currently have is:

I have created the configuration and controllers inside loadView(), so by the time it reaches viewDidLoad() everything gets displayed on the app.

 override func loadView(){
        super.loadView()

        let contentController = WKUserContentController()
        contentController.add(self as WKScriptMessageHandler, name: "callbackHandlerFile")

        // configuration
        let config = WKWebViewConfiguration()
        config.userContentController = contentController
        self.webView = WKWebView(frame: self.view.frame, configuration: config)
        self.view = self.webView

    }

    override func viewDidLoad() {
        super.viewDidLoad()
        let url = NSURL(fileURLWithPath: Bundle.main.path(forResource: "html/_wv", ofType: "html")!)
        let req = URLRequest(url: url as URL)
        self.webView!.load(req)

    }

My issue comes from the fact that I have seen that the way in which information is send from Javascript to swift is like this:

function sendInfo(fileName, dataContent){
    webkit.messageHandlers.callbackHandlerFile.postMessage(fileName);
    webkit.messageHandlers.callbackHandlerFile.postMessage(dataContent);
}

And then we retrieve them as:

 // message handler delegate method
func userContentController(_ userContentController: WKUserContentController, didReceive message:WKScriptMessage){
    if(message.name == "callbackHandlerFile") {
            print(message.body)    
    }
}

The print statement will print out both messages one after another, meaning that it will print the contents of fileName and then the contents of dataContent because all the data got mashed up on the same instance.

How would I go by obtaining the same effect as the android counterpart? I need to have these two parts on the same place since I need to create some file i/o, so having different callbackHandlers is pretty much out of the question.All I need is to have something that resembles the Android version. Any pointers would be greatly appreciated.

EDIT:

I know that I can pass objects to the message handlers, so I would have something like:

webkit.messageHandlers.callbackHandlerFile.postMessage({fileName: file,data: data_txt});

But still cant't find a way to access this data without the compiler giving me errors since message.body works with Strings


Solution

  • You can handle this creating a JSON Object with both data and the action you want to do.

    Example:

    { "action" : "saveData", "data": { "fileName": fileName, "dataContent" : dataContent } }

    Then handle the response as a Dictionary like this:

    // message handler delegate method
    func userContentController(_ userContentController: WKUserContentController, didReceive message:WKScriptMessage){
            guard let body = message.body as? [String: Any] else {
                return
            }
    }