В iOS 14 представлен новый способ получения вызовов javascript и предоставления ответа с использованием WKScriptMessageHandlerWithReply вместо WKScriptMessageHandler (внутри представления WebKit). Однако документация практически отсутствует. Как это работает?
Я немного покопался в этом и обнаружил, что он использует обещания Javascript для обеспечения механизма обратного вызова (и ответ кода приложения обратно на javascript должен быть асинхронным).
Вот пример кода для иллюстрации:
Быстрый код:
import UIKit
import WebKit
import PureLayout
final class ViewController: UIViewController {
var webView : WKWebView?
let JavaScriptAPIObjectName = "namespaceWithinTheInjectedJSCode"
override func viewDidLoad() {
super.viewDidLoad()
//-------
guard let scriptPath = Bundle.main.path(forResource: "script", ofType: "js"),
let scriptSource = try? String(contentsOfFile: scriptPath) else { return }
let userScript = WKUserScript(source: scriptSource, injectionTime: .atDocumentEnd, forMainFrameOnly: true)
let config = WKWebViewConfiguration()
let userContentController = WKUserContentController()
userContentController.addUserScript(userScript)
// REQUIRES IOS14
if #available(iOS 14, *){
userContentController.addScriptMessageHandler(self, contentWorld: .page, name: JavaScriptAPIObjectName)
}
config.userContentController = userContentController
webView = WKWebView(frame: .zero, configuration: config)
if let webView = webView{
view.addSubview(webView)
webView.autoPinEdgesToSuperviewMargins() // using PureLayout for easy AutoLayout syntax
if let htmlPath = Bundle.main.url(forResource: "page", withExtension: "html"){
webView.loadFileURL( htmlPath, allowingReadAccessTo: htmlPath);
}
}
}
// need to deinit and remove webview stuff
deinit {
if let webView = webView{
let ucc = webView.configuration.userContentController
ucc.removeAllUserScripts()
ucc.removeScriptMessageHandler(forName:JavaScriptAPIObjectName)
}
}
}
extension ViewController: WKScriptMessageHandlerWithReply {
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage, replyHandler: @escaping (Any?, String?) -> Void) {
if message.name == JavaScriptAPIObjectName, let messageBody = message.body as? String {
print(messageBody)
replyHandler( 2.2, nil ) // first var is success return val, second is err string if error
}
}
}
Это script.js, загруженный с помощью этого кода Swift и внедренный на веб-страницу:
function sampleMethodTheHTMLCanCall( inputInfo, successFunc, errorFunc ) {
var promise = window.webkit.messageHandlers.namespaceWithinTheInjectedJSCode.postMessage( inputInfo );
promise.then(
function(result) {
console.info(result); // "Stuff worked!"
successFunc( result )
},
function(err) {
console.info(err); // Error: "It broke"
errorFunc( err )
});
}
А вот пример HTML page.html, который может вызывать код приложения:
<html>
<meta name = "viewport" content = "width=device-width" />
<script>
function handleInfoFromApp( fromApp ){
document.getElementById("valToWrite").innerHTML = fromApp;
}
function handleError( err ){
}
</script>
<h1 id = "valToWrite">Hello</h1>
<button onclick = "sampleMethodTheHTMLCanCall( 'inputInfo', handleInfoFromApp, handleError )">Load Info from App</button>
</html>
В приведенном выше HTML-коде представлены функции, которые впоследствии будут вызываться кодом расширения приложения при успешном или неудачном выполнении запроса, инициированного javascript.
Эй, @Jason, я знаю, что это взрыв из прошлого, но мне интересно, не могли бы вы объяснить, почему вы добавили удаление скрипта в функцию deinit. Мне интересно, почему это должно произойти. Спасибо!
Этот ответ должен быть принят, на мой взгляд. Большое спасибо!!!!!