Call JavaScript Function from Native Code in Wkwebview

Call JavaScript function from native code in WKWebView

(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.

How to call a javascript function from WKWebview when message is received from webapp

I finally solved the issue

Problem

I was implementing my configuration and controller in view.didLoad() func.

Solution

Add all the configuration , preferences and userContentController in loadView() func and load webview either by https endpoint or your local html file in viewDidLoad() func


Load View

override func loadView() {
super.loadView()

let preferences = WKPreferences()
preferences.javaScriptEnabled = true

let configuration = WKWebViewConfiguration()
configuration.preferences = preferences

let userContentController = WKUserContentController()

userContentController.add(self,name:"sendComment")

configuration.userContentController = userContentController

self.view.addSubview(webView)
self.view.sendSubviewToBack(webView)

webView = WKWebView(frame: view.bounds, configuration: configuration)

view = webView
}

View Did Load

Here is my override func viewDidLoad()

override func viewDidLoad() {
super.viewDidLoad()
print("view will load")
// Do any additional setup after loading the view, typically from a nib.

let request = URLRequest(url:URL(string:"your-url")!)
webView.navigationDelegate = self

webView.load(request)
}

userContentController

My userContentController for receiving JS messages inside the viewController class

    func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
print(message.name)
if message.name == "sendComment" {
let result:String = "Marlboro"

webView.evaluateJavascript("sendComment('\(result)')", completionHandler:{(result , error) in
if error == nil {
print("success")
}
else {
print("error in executing js ", error)
}
})
}
}

Call native swift function in javascript

You need to inject your user script inside the webView so you could listen to JavaScript's messages when they are posted.

Firstly, you will need to initialize the webView's config and preferences, to allow javaScript:

let config = WKWebViewConfiguration()
let preferences = WKPreferences()
preferences.javaScriptEnabled = true
config.preferences = preferences

Now, you need to inject your script to listen for the click events from the webView:

let userController = WKUserContentController()
let javaScript = "showRewardsAd = function() {\n" + "window.webkit.messageHandlers.adClicked.postMessage(); }"
let script = WKUserScript(source: javaScript, injectionTime: .atDocumentEnd, forMainFrameOnly: false)
userController.addUserScript(script)
userController.add(self, name: "adClicked")
config.userContentController = userController

Now instantiate your webView with the configuration from above:

self.webView = WKWebView(frame: .zero, configuration: config)

Now, your WKScriptMessageHandler protocol is implemented correctly:

extension WFWebViewController: WKScriptMessageHandler{
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
if message.name == "showRewardsAd"{
self.showRewards()
print("Ad is being shown.")
}
}
}

I hope this works for you. I assumed that showRewardsAd is implemented in the JavaScript on the website which you're accessing.

WKWebview - Complex communication between Javascript & native code

Unfortunately I couldn't find a native solution.

But the following workaround solved my problem

Use javascript promises & you can call the resolve function from your iOS code.

UPDATE

This is how you can use promise

In JS

   this.id = 1;
this.handlers = {};

window.onMessageReceive = (handle, error, data) => {
if (error){
this.handlers[handle].resolve(data);
}else{
this.handlers[handle].reject(data);
}
delete this.handlers[handle];
};
}

sendMessage(data) {
return new Promise((resolve, reject) => {
const handle = 'm'+ this.id++;
this.handlers[handle] = { resolve, reject};
window.webkit.messageHandlers.<yourHandler>.postMessage({data: data, id: handle});
});
}

in iOS

Call the window.onMessageReceive function with appropriate handler id

Call a swift function from java script that returns a value using wk webview

Try having a look at the evaluateJavaScript(_:, completionHandler:) function on WKWebView as described here

To use that, your DeviceInfo function should define the complete JavaScript string which you would like to execute.

So for instance if you had a JavaScript function defined like so:

showDeviceInfo(model, name, system_version) {

}

Then your DeviceInfo function could look something like this:

func deviceInfo() {
let model = UIDevice.currentDevice().model
let name = UIDevice.currentDevice().name
let systemVersion = UIDevice.currentDevice().systemVersion

let javaScriptString = "showDeviceInfo(\(model), \(name), \(systemVersion));" //string your JavaScript string together from the previous info...and no...it aint pretty :)
webView.evaluateJavaScript(javaScriptString, completionHandler: nil)
}

You could also return javaScriptString from your DeviceInfo function and then call it in your userContentController(userContentController: WKUserContentController, didReceiveScriptMessage message: WKScriptMessage), the important things are:

  1. you need to define the entire JavaScript string you would like to execute
  2. you need to call evaluateJavaScript

Hope that helps you.



Related Topics



Leave a reply



Submit