Возможна ли синхронная связь между JavaScript и собственным кодом Swift / Obj-C с помощью WKWebView?
Это подходы, которые я пробовал и потерпел неудачу.
Подход 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
Пример (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")
}
var isTwitterInstalled = isInstalled('twitter');
. Если у вас есть только асинхронные вызовы, вам придется разделить их на две функции; один, который вы вызываете из JavaScript, а другой, который вы вызываете из собственного кода, передавая результат. - person paulvs   schedule 23.02.2015window
? - person Steve Landey   schedule 28.03.2015var something;
на самом деле лишний, потому что инъекция из собственного кода создаетsomething
как глобальный, когда он присваивает ему свое значение. - person paulvs   schedule 30.03.2015var something
затмевает глобальное. Это не лишнее, это баг. - person Steve Landey   schedule 30.03.2015var something;
не находится внутри какой-либо функции, т.е. она находится в глобальной области видимости, она создает глобальную переменную с именемsomething
(эквивалентwindow.something = undefined;
). Затем собственный код присваивает42
глобальной переменнойwindow.something
(и если глобальная переменная не существует, он сначала создает ее, а затем присваивает ей значение). Итак, создадим ли мы объявлениеsomething
заранее или нет, оно создается собственным кодом, верно? - person paulvs   schedule 31.03.2015window.location
илиiframe
? - person Maria   schedule 16.10.2018