Catch JavaScript Event in iOS Wkwebview with Swift

Catch Javascript Event in iOS WKWebview with Swift

Based on the answer from @Alex Pelletier, which really helped me, here is the solution the my question.

In my "loadView()" function, here is what I have :

let contentController = WKUserContentController();
contentController.addScriptMessageHandler(
self,
name: "callbackHandler"
)

let config = WKWebViewConfiguration()
config.userContentController = contentController

webView = WKWebView(frame: CGRectZero, configuration: config)
webView.navigationDelegate = self
view = webView

My function to handle the Javascript event which is sent to Swift :

func userContentController(userContentController: WKUserContentController, didReceiveScriptMessage message: WKScriptMessage)
{
if(message.name == "callbackHandler") {
print("Launch my Native Camera")
}
}

... And finally, my Javascript (jQuery) code when a click happens on my camera button (in HTML) :

$(document).ready(function() {

function callNativeApp () {
try {
webkit.messageHandlers.callbackHandler.postMessage("camera");
} catch(err) {
console.log('The native context does not exist yet');
}
}

$(".menu-camera-icon").click(function() {
callNativeApp();
});
});

I hope it will help someone else :-) !

Handling JavaScript events in WKWebView

you can use a WKUserScript and add it to the userContentController of the WKWebView's configuration.

    let config = WKWebViewConfiguration()
let source = "document.addEventListener('click', function(){ window.webkit.messageHandlers.iosListener.postMessage('click clack!'); })"
let script = WKUserScript(source: source, injectionTime: .atDocumentEnd, forMainFrameOnly: false)
config.userContentController.addUserScript(script)
config.userContentController.add(self, name: "iosListener")
webView = WKWebView(frame: UIScreen.main.bounds, configuration: config)

this will make the script and inject it into the page when the document is finished loading. Now, you need to implement the WKScriptMessageHandler protocol to receive the message:

    func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
print("message: \(message.body)")
// and whatever other actions you want to take
}

How to inject JavaScript callback to detect onclick event, using iOS WKWebView?

User Scripts are JS that you inject into your web page at either the start of the document load or after the document is done loading. User scripts are extremely powerful because they allow client-side customization of web page, allow injection of event listeners and can even be used to inject scripts that can in turn call back into the Native app. The following code snippet creates a user script that is injected at end of document load. The user script is added to the WKUserContentController instance that is a property on the WKWebViewConfiguration object.

// Create WKWebViewConfiguration instance
var webCfg:WKWebViewConfiguration = WKWebViewConfiguration()

// Setup WKUserContentController instance for injecting user script
var userController:WKUserContentController = WKUserContentController()

// Get script that's to be injected into the document
let js:String = buttonClickEventTriggeredScriptToAddToDocument()

// Specify when and where and what user script needs to be injected into the web document
var userScript:WKUserScript = WKUserScript(source: js,
injectionTime: WKUserScriptInjectionTime.atDocumentEnd,
forMainFrameOnly: false)

// Add the user script to the WKUserContentController instance
userController.addUserScript(userScript)

// Configure the WKWebViewConfiguration instance with the WKUserContentController
webCfg.userContentController = userController;

Your web page can post messages to your native app via the window.webkit.messageHandlers.<name>.postMessage (<message body>) method.
Here, “name” is the name of the message being posted back. The JS can post back any JS object as message body and the JS object would be automatically mapped to corresponding Swift native object.
The following JS code snippet posts back a message when a button click event occurs on a button with Id “ClickMeButton”.

var button = document.getElementById("clickMeButton");
button.addEventListener("click", function() {
varmessageToPost = {'ButtonId':'clickMeButton'};
window.webkit.messageHandlers.buttonClicked.postMessage(messageToPost);
},false);

In order to receive messages posted by your web page, your native app needs to implement the WKScriptMessageHandler protocol.
The protocol defines a single required method. The WKScriptMessage instance returned in the callback can be queried for details on the message being posted back.

func userContentController(userContentController: WKUserContentController,
didReceiveScriptMessage message: WKScriptMessage) {

if let messageBody:NSDictionary= message.body as? NSDictionary{
// Do stuff with messageBody
}

}

Finally, the native class that implements WKScriptMessageHandler protocol needs to register itself as a message handler with the WKWebView as follows:

// Add a script message handler for receiving  "buttonClicked" event notifications posted 
// from the JS document
userController.addScriptMessageHandler(self, name: "buttonClicked")

WKWebView get Javascript Errors

To handle errors, we are going to add some JavaScript to the HTML we load into our page that will notify our native code about the error.

First, you need to setup your web view with a script message handler:

let controller = WKUserContentController()
controller.add(self, name: "error")

let configuration = WKWebViewConfiguration()
configuration.userContentController = controller

let webView = WKWebView(frame: .zero, configuration: configuration)

I locally create the full HTML document and inject the following JavaScript:

window.onerror = (msg, url, line, column, error) => {
const message = {
message: msg,
url: url,
line: line,
column: column,
error: JSON.stringify(error)
}

if (window.webkit) {
window.webkit.messageHandlers.error.postMessage(message);
} else {
console.log("Error:", message);
}
};

(I have the check and log since I sometime run the generated JavaScript in a browser.) You could also use the WKUserContentController to add a WKUserScript if you want to inject it into HTML you don’t fully control.

Next when you load a string into your web view, be sure to use localhost as the base URL. If you don’t do this, all of your errors will be "Script error.".

webView.loadHTMLString(html, baseURL: URL(string: "http://localhost/")!)

Now define your WKScriptMessageHandler:

func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
switch message.name {
case "error":
// You should actually handle the error :)
let error = (message.body as? [String: Any])?["message"] as? String ?? "unknown"
assertionFailure("JavaScript error: \(error)")
default:
assertionFailure("Received invalid message: \(message.name)")
}
}

You can add other cases to the switch statement for other messages you add to the WKUserContentController.



Related Topics



Leave a reply



Submit