Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WKWebview - Complex communication between Javascript & native code

In WKWebView we can call ObjectiveC/swift code using webkit message handlers eg: webkit.messageHandlers.<handler>.pushMessage(message)

It works well for simple javascript functions without parameters. But;

  1. Is it possible to call native code with JS callback function as parameters?
  2. Is it possible to return a value to JS function from native code?
like image 823
Clement Prem Avatar asked Mar 25 '15 06:03

Clement Prem


Video Answer


2 Answers

Unfortunately I couldn't find a native solution.

But the following workaround solved my problem

Use javascript promises & you can call the resolve function from your iOS code.

UPDATE

This is how you can use promise

In JS

   this.id = 1;     this.handlers = {};      window.onMessageReceive = (handle, error, data) => {       if (error){         this.handlers[handle].resolve(data);       }else{         this.handlers[handle].reject(data);       }       delete this.handlers[handle];     };   }    sendMessage(data) {     return new Promise((resolve, reject) => {       const handle = 'm'+ this.id++;       this.handlers[handle] = { resolve, reject};       window.webkit.messageHandlers.<yourHandler>.postMessage({data: data, id: handle});     });   } 

in iOS

Call the window.onMessageReceive function with appropriate handler id

like image 112
Clement Prem Avatar answered Oct 05 '22 13:10

Clement Prem


There is a way to get a return value back to JS from the native code using WkWebView. It is a little hack but works fine for me without problems, and our production app uses a lot of JS/Native communication.

In the WKUiDelegate assigned to the WKWebView, override the RunJavaScriptTextInputPanel. This uses the way that the delegate handles the JS prompt function to accomplish this:

    public override void RunJavaScriptTextInputPanel (WebKit.WKWebView webView, string prompt, string defaultText, WebKit.WKFrameInfo frame, Action<string> completionHandler)     {         // this is used to pass synchronous messages to the ui (instead of the script handler). This is because the script          // handler cannot return a value...         if (prompt.StartsWith ("type=", StringComparison.CurrentCultureIgnoreCase)) {             string result = ToUiSynch (prompt);             completionHandler.Invoke ((result == null) ? "" : result);         } else {             // actually run an input panel             base.RunJavaScriptTextInputPanel (webView, prompt, defaultText, frame, completionHandler);             //MobApp.DisplayAlert ("EXCEPTION", "Input panel not implemented.");          }     } 

In my case, I am passing data type=xyz,name=xyz,data=xyz to pass the args in. My ToUiSynch() code handles the request and always returns a string, which goes back to the JS as a simple return value.

In the JS, I am simply calling the prompt() function with my formatted args string and getting a return value:

return prompt ("type=" + type + ";name=" + name + ";data=" + (typeof data === "object" ? JSON.stringify ( data ) : data )); 
like image 30
Nathan Brown Avatar answered Oct 05 '22 13:10

Nathan Brown