Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JavaScript synchronous native communication to WKWebView

Is synchronous communication between JavaScript and Swift/Obj-C native code possible using the WKWebView?

These are the approaches I have tried and have failed.

Approach 1: Using script handlers

WKWebView's new way of receiving JS messages is by using the delegate method userContentController:didReceiveScriptMessage: which is invoked from JS by window.webkit.messageHandlers.myMsgHandler.postMessage('What's the meaning of life, native code?') The problem with this approach is that during execution of the native delegate method, JS execution is not blocked, so we can't return a value by immediately invoking webView.evaluateJavaScript("something = 42", completionHandler: nil).

Example (JavaScript)

var something; function getSomething() {     window.webkit.messageHandlers.myMsgHandler.postMessage("What's the meaning of life, native code?"); // Execution NOT blocking here :(     return something; } getSomething();    // Returns undefined 

Example (Swift)

func userContentController(userContentController: WKUserContentController, didReceiveScriptMessage message: WKScriptMessage) {     webView.evaluateJavaScript("something = 42", completionHandler: nil) } 

Approach 2: Using a custom URL scheme

In JS, redirecting using window.location = "js://webView?hello=world" invokes the native WKNavigationDelegate methods, where the URL query parameters can be extracted. However, unlike the UIWebView, the delegate method is not blocking the JS execution, so immediately invoking evaluateJavaScript to pass a value back to the JS doesn't work here either.

Example (JavaScript)

var something; function getSomething() {     window.location = "js://webView?question=meaning" // Execution NOT blocking here either :(     return something; } getSomething(); // Returns undefined 

Example (Swift)

func webView(webView: WKWebView, decidePolicyForNavigationAction navigationAction: WKNavigationAction, decisionHandler decisionHandler: (WKNavigationActionPolicy) -> Void) {     webView.evaluateJavaScript("something = 42", completionHandler: nil)     decisionHandler(WKNavigationActionPolicy.Allow) } 

Approach 3: Using a custom URL scheme and an IFRAME

This approach only differs in the way that window.location is assigned. Instead of assigning it directly, the src attribute of an empty iframe is used.

Example (JavaScript)

var something; function getSomething() {     var iframe = document.createElement("IFRAME");     iframe.setAttribute("src", "js://webView?hello=world");     document.documentElement.appendChild(iframe);  // Execution NOT blocking here either :(     iframe.parentNode.removeChild(iframe);     iframe = null;     return something; } getSomething(); 

This nonetheless, is not a solution either, it invokes the same native method as Approach 2, which is not synchronous.

Appendix: How to achieve this with the old UIWebView

Example (JavaScript)

var something; function getSomething() {     // window.location = "js://webView?question=meaning" // Execution is NOT blocking if you use this.      // Execution IS BLOCKING if you use this.     var iframe = document.createElement("IFRAME");     iframe.setAttribute("src", "js://webView?question=meaning");     document.documentElement.appendChild(iframe);     iframe.parentNode.removeChild(iframe);     iframe = null;      return something; } getSomething();   // Returns 42 

Example (Swift)

func webView(webView: UIWebView, shouldStartLoadWithRequest request: NSURLRequest, navigationType: UIWebViewNavigationType) -> Bool {     webView.stringByEvaluatingJavaScriptFromString("something = 42")     } 
like image 743
paulvs Avatar asked Nov 10 '14 19:11

paulvs


People also ask

What is the difference between UIWebView and WKWebView?

Unlike UIWebView, which does not support server authentication challenges, WKWebView does. In practical terms, this means that when using WKWebView, you can enter site credentials for password-protected websites.

What is WKWebView?

A WKWebView object is a platform-native view that you use to incorporate web content seamlessly into your app's UI. A web view supports a full web-browsing experience, and presents HTML, CSS, and JavaScript content alongside your app's native views.

How do I import WKWebView into Objective C?

You can implement WKWebView in Objective-C, here is simple example to initiate a WKWebView : WKWebViewConfiguration *theConfiguration = [[WKWebViewConfiguration alloc] init]; WKWebView *webView = [[WKWebView alloc] initWithFrame:self. view. frame configuration:theConfiguration]; webView.

How do I use WKWebView?

Here's how: Open the XIB or Storyboard you want to add the web view to in Interface Builder. Find the web view or WKWebView in the Object Library at the bottom-left of Interface Builder. Drag-and-drop a WKWebView object from the Object Library to your view controller's canvas, and adjust its size and position.


2 Answers

No I don't believe it is possible due to the multi-process architecture of WKWebView. WKWebView runs in the same process as your application but it communicates with WebKit which runs in its own process (Introducing the Modern WebKit API). The JavaScript code will be running in the WebKit process. So essentially you are asking to have synchronous communication between two different processes which goes against their design.

like image 63
Martin Sherburn Avatar answered Sep 22 '22 06:09

Martin Sherburn


I found a hack for doing synchronous communication but haven't tried it yet: https://stackoverflow.com/a/49474323/2870783

Edit: Basically you can use the the JS prompt() to carry your payload from the js side to the native side. In the native WKWebView will have to intercept the prompt call and decide if it is a normal call or if it is a jsbridge call. Then you can return your result as a callback to the prompt call. Because the prompt call is implemented in such a way that it waits for user input your javascript-native communication will be synchronous. The downside is that you can only communicate trough strings.

like image 26
h3dkandi Avatar answered Sep 23 '22 06:09

h3dkandi