与WKWebView的JavaScript同步本机通信

pau*_*lvs 26 swift ios8 wkwebview

使用WKWebView可以在JavaScript和Swift/Obj-C本机代码之间进行同步通信吗?

这些是我尝试过但失败的方法.

方法1:使用脚本处理程序

WKWebView接收JS消息的新方法是使用userContentController:didReceiveScriptMessage:从JS调用的委托方法window.webkit.messageHandlers.myMsgHandler.postMessage('What's the meaning of life, native code?') .这种方法的问题是在执行本机委托方法时,JS执行没有被阻止,所以我们不能通过立即调用webView.evaluateJavaScript("something = 42", completionHandler: nil).

示例(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
Run Code Online (Sandbox Code Playgroud)

示例(Swift)

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

方法2:使用自定义URL方案

在JS中,重定向使用window.location = "js://webView?hello=world"调用本机 WKNavigationDelegate方法,其中可以提取URL查询参数.但是,与UIWebView不同,委托方法不会阻止JS执行,因此立即调用evaluateJavaScript将值传递回JS也不起作用.

示例(JavaScript)

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

示例(Swift)

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

方法3:使用自定义URL方案和IFRAME

这种方法仅在window.location分配方式上有所不同.不使用直接分配,而是使用src空的属性iframe.

示例(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();
Run Code Online (Sandbox Code Playgroud)

尽管如此,它也不是解决方案,它调用与方法2相同的本机方法,方法2不同步.

附录:如何使用旧的UIWebView实现此目的

示例(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
Run Code Online (Sandbox Code Playgroud)

示例(Swift)

func webView(webView: UIWebView, shouldStartLoadWithRequest request: NSURLRequest, navigationType: UIWebViewNavigationType) -> Bool {
    webView.stringByEvaluatingJavaScriptFromString("something = 42")    
}
Run Code Online (Sandbox Code Playgroud)

Mar*_*urn 9

不,由于WKWebView的多进程架构,我不相信它是可能的.WKWebView在与应用程序相同的进程中运行,但它与在自己的进程中运行的WebKit进行通信(介绍Modern WebKit API).JavaScript代码将在WebKit进程中运行.所以基本上你要求在两个不同的进程之间进行同步通信,这违背了他们的设计.

  • 像WKWebView这样的闪亮新API缺乏Android多年来的功能,这真是一个耻辱. (4认同)

h3d*_*ndi 7

我发现了一种进行同步通信的技巧,但尚未尝试:https ://stackoverflow.com/a/49474323/2870783

编辑:基本上,您可以使用 JS Prompt() 将有效负载从 js 端传送到本机端。在原生WKWebView中,必须拦截提示调用并判断它是普通调用还是jsbridge调用。然后您可以将结果作为提示调用的回调返回。因为提示调用是以等待用户输入的方式实现的,所以您的 javascript 原生通信将是同步的。缺点是只能通过字符串进行通信。