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
例(スウィフト)
func userContentController(userContentController: WKUserContentController, didReceiveScriptMessage message: WKScriptMessage) {
webView.evaluateJavaScript("something = 42", completionHandler: nil)
}
アプローチ 2: カスタム URL スキームを使用する
JS では、URL クエリ パラメータを抽出できるネイティブ メソッドを使用してリダイレクトするとwindow.location = "js://webView?hello=world"
、ネイティブ メソッドが呼び出さ れます。WKNavigationDelegate
ただし、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
例(スウィフト)
func webView(webView: WKWebView, decidePolicyForNavigationAction navigationAction: WKNavigationAction, decisionHandler decisionHandler: (WKNavigationActionPolicy) -> Void) {
webView.evaluateJavaScript("something = 42", completionHandler: nil)
decisionHandler(WKNavigationActionPolicy.Allow)
}
アプローチ 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();
それにもかかわらず、これも解決策ではありません。アプローチ 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
例(スウィフト)
func webView(webView: UIWebView, shouldStartLoadWithRequest request: NSURLRequest, navigationType: UIWebViewNavigationType) -> Bool {
webView.stringByEvaluatingJavaScriptFromString("something = 42")
}