Возможно ли использование синхронной связи между JavaScript и Swift/Obj-C с помощью WKWebView?
Это те подходы, которые я пробовал и потерпел неудачу.
Подход 1: Использование обработчиков script
WKWebView
Новый способ получения сообщений JS заключается в использовании метода делегата userContentController:didReceiveScriptMessage:
, который вызывается из JS на window.webkit.messageHandlers.myMsgHandler.postMessage('What the meaning of life, native code?')
Проблема с этим подходом заключается в том, что во время выполнения собственного метода делегирования выполнение JS не блокируется, поэтому мы не можем вернуть значение, немедленно вызывая webView.evaluateJavaScript("something = 42", completionHandler: nil)
.
Пример (JavaScript)
var something;
function getSomething() {
window.webkit.messageHandlers.myMsgHandler.postMessage("What the meaning of life, native code?"); // Execution NOT blocking here :(
return something;
}
getSomething(); // Returns undefined
Пример (Swift)
func userContentController(userContentController: WKUserContentController, didReceiveScriptMessage message: WKScriptMessage) {
webView.evaluateJavaScript("something = 42", completionHandler: nil)
}
Подход 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
Пример (Swift)
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
Пример (Swift)
func webView(webView: UIWebView, shouldStartLoadWithRequest request: NSURLRequest, navigationType: UIWebViewNavigationType) -> Bool {
webView.stringByEvaluatingJavaScriptFromString("something = 42")
}