Using a WKWebView in iOS 8, how can I run a JavaScript function from the native side or otherwise communicate from the native side to JavaScript? There doesn't appear to be a method akin to UIWebView's stringByEvaluatingJavaScriptFromString:
.
(I can use - addScriptMessageHandler:name:
on the configuration.userContentController
object to allow communication from JS to native, but I'm looking for the opposite direction.)
Overview. A WKUserContentController object provides a bridge between your app and the JavaScript code running in the web view. Use this object to do the following: Inject JavaScript code into webpages running in your web view. Install custom JavaScript functions that call through to your app's native code.
A script that the web view injects into a webpage.
(I filed a Radar for this shortly after asking the question here.)
A new method was just added a few days ago (thanks jcesarmobile for pointing it out):
Add
-[WKWebView evaluateJavaScript:completionHandler:]
http://trac.webkit.org/changeset/169765
The method is available in iOS 8 beta 3 and up. Here's the new method signature:
/* @abstract Evaluates the given JavaScript string. @param javaScriptString The JavaScript string to evaluate. @param completionHandler A block to invoke when script evaluation completes or fails. @discussion The completionHandler is passed the result of the script evaluation or an error. */ - (void)evaluateJavaScript:(NSString *)javaScriptString completionHandler:(void (^)(id, NSError *))completionHandler;
Docs are available here: https://developer.apple.com/documentation/webkit/wkwebview/1415017-evaluatejavascript.
The script is inserted into page which will displayed in WKWebView. This script will return the page URL (but you can write another JavaScript code). This means that the script event is generated on the web page, but it will be handled in our function:
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {...}
extension WKUserScript { enum Defined: String { case getUrlAtDocumentStartScript = "GetUrlAtDocumentStart" case getUrlAtDocumentEndScript = "GetUrlAtDocumentEnd" var name: String { return rawValue } private var injectionTime: WKUserScriptInjectionTime { switch self { case .getUrlAtDocumentStartScript: return .atDocumentStart case .getUrlAtDocumentEndScript: return .atDocumentEnd } } private var forMainFrameOnly: Bool { switch self { case .getUrlAtDocumentStartScript: return false case .getUrlAtDocumentEndScript: return false } } private var source: String { switch self { case .getUrlAtDocumentEndScript, .getUrlAtDocumentStartScript: return "webkit.messageHandlers.\(name).postMessage(document.URL)" } } fileprivate func create() -> WKUserScript { return WKUserScript(source: source, injectionTime: injectionTime, forMainFrameOnly: forMainFrameOnly) } } } extension WKWebViewConfiguration { func add(script: WKUserScript.Defined, scriptMessageHandler: WKScriptMessageHandler) { userContentController.addUserScript(script.create()) userContentController.add(scriptMessageHandler, name: script.name) } }
Init WKWebView
let config = WKWebViewConfiguration() config.add(script: .getUrlAtDocumentStartScript, scriptMessageHandler: self) config.add(script: .getUrlAtDocumentEndScript, scriptMessageHandler: self) webView = WKWebView(frame: UIScreen.main.bounds, configuration: config) webView.navigationDelegate = self view.addSubview(webView)
Catch events
extension ViewController: WKScriptMessageHandler { func userContentController (_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) { if let script = WKUserScript.Defined(rawValue: message.name), let url = message.webView?.url { switch script { case .getUrlAtDocumentStartScript: print("start: \(url)") case .getUrlAtDocumentEndScript: print("end: \(url)") } } } }
import UIKit import WebKit class ViewController: UIViewController, WKNavigationDelegate { private var webView = WKWebView() override func viewDidLoad() { super.viewDidLoad() let config = WKWebViewConfiguration() config.add(script: .getUrlAtDocumentStartScript, scriptMessageHandler: self) config.add(script: .getUrlAtDocumentEndScript, scriptMessageHandler: self) webView = WKWebView(frame: UIScreen.main.bounds, configuration: config) webView.navigationDelegate = self view.addSubview(webView) } override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) webView.load(urlString: "http://apple.com") } } extension ViewController: WKScriptMessageHandler { func userContentController (_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) { if let script = WKUserScript.Defined(rawValue: message.name), let url = message.webView?.url { switch script { case .getUrlAtDocumentStartScript: print("start: \(url)") case .getUrlAtDocumentEndScript: print("end: \(url)") } } } } extension WKWebView { func load(urlString: String) { if let url = URL(string: urlString) { load(URLRequest(url: url)) } } } extension WKUserScript { enum Defined: String { case getUrlAtDocumentStartScript = "GetUrlAtDocumentStart" case getUrlAtDocumentEndScript = "GetUrlAtDocumentEnd" var name: String { return rawValue } private var injectionTime: WKUserScriptInjectionTime { switch self { case .getUrlAtDocumentStartScript: return .atDocumentStart case .getUrlAtDocumentEndScript: return .atDocumentEnd } } private var forMainFrameOnly: Bool { switch self { case .getUrlAtDocumentStartScript: return false case .getUrlAtDocumentEndScript: return false } } private var source: String { switch self { case .getUrlAtDocumentEndScript, .getUrlAtDocumentStartScript: return "webkit.messageHandlers.\(name).postMessage(document.URL)" } } fileprivate func create() -> WKUserScript { return WKUserScript(source: source, injectionTime: injectionTime, forMainFrameOnly: forMainFrameOnly) } } } extension WKWebViewConfiguration { func add(script: WKUserScript.Defined, scriptMessageHandler: WKScriptMessageHandler) { userContentController.addUserScript(script.create()) userContentController.add(scriptMessageHandler, name: script.name) } }
add in your Info.plist transport security setting
<key>NSAppTransportSecurity</key> <dict> <key>NSAllowsArbitraryLoads</key> <true/> </dict>
Document Object Properties and Methods
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With