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:
- you need to define the entire JavaScript string you would like to execute
- you need to call
evaluateJavaScript
Hope that helps you.
Related Topics
Difference Between Synchronous and Asynchronous Programming (In Node.Js)
How to Get the Coordinates of a Mouse Click on a Canvas Element
Is the JavaScript Date Object Always One Day Off
How to Pass Parameters Rendered from Backend to Angular2 Bootstrap Method
How to Make Console.Log Show the Current State of an Object
Console.Log() Shows the Changed Value of a Variable Before the Value Actually Changes
Check If an Array Contains Any Element of Another Array in JavaScript
No Visible Cause For "Unexpected Token Illegal"
Ecmascript Template Literals Like 'Some ${String}' Are Not Working
How to Connect to SQL Server Database from JavaScript in the Browser
Why Is It Necessary to Set the Prototype Constructor
How to Check Whether a Radio Button Is Selected With JavaScript
How to Parse a CSV String With JavaScript, Which Contains Comma in Data
Create a Date With a Set Timezone Without Using a String Representation
Creating Object With Dynamic Keys
Can't Perform a React State Update on an Unmounted Component
Convert Dropdown to Selection Boxes with Color and Trigger Drop Down Action