Catch events javascript in swift - javascript

In android i use this
https://developer.android.com/reference/android/webkit/JavascriptInterface
to catch the events javascript in java
but in swift, what can i use ?
i just found to execute javascript for swift, but i want catch a click event.
Example, when the user click in button, change the screen according the option selected.

You can use WKWebView for this
let contentController = WKUserContentController()
contentController.add(self, name: scriptMessageName)
let config = WKWebViewConfiguration()
config.userContentController = contentController
let webView = WKWebView(frame: self.view.frame, configuration: config)
Then confirm you ViewController to WKScriptMessageHandler, WKNavigationDelegate
extension ViewController: WKScriptMessageHandler, WKNavigationDelegate {
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
switch message.name {
case scriptMessageName:
someAction()
default:
break
}
}
}

Related

SwiftUI: Javascript communication with WKWebView on macOS

I want javascript to send a message back to my WKWebView object, but I get nothing in response. I know the script is running, as the color changes, but I am expecting to also see "trigger from JS" printed in the console, which I don't. If I run the html in Chrome, the javascript console says "Cannot read property 'messageHandlers' of undefined". If I build for iOS (using UIViewRepresentable, MakeUIView and UpdateUIView) the result is the same. If anyone can spot what I have missed I would greatly appreciate it.
This is the entirety of the code:
import SwiftUI
import WebKit
class HtmlData {
let html = """
<!DOCTYPE html>
<html>
<body>
<button onclick="sendMessage()">Send Message</button>
<script>
function sendMessage() {
document.body.style.backgroundColor = "red";
window.webkit.messageHandlers.testMessage.postMessage("trigger from JS");
}
</script>
</body>
</html>
"""
}
struct ContentView: View {
let htmlData = HtmlData()
var body: some View {
JSWebView(html: htmlData.html)
}
}
struct JSWebView: NSViewRepresentable {
let html: String
func makeNSView(context: Context) -> WKWebView {
let preferences = WKPreferences()
preferences.javaScriptEnabled = true
return WKWebView()
}
func updateNSView(_ view: WKWebView, context: Context) {
let userContentController = WKUserContentController()
let handler = ContentController(view)
let configuration = WKWebViewConfiguration()
configuration.userContentController = userContentController
configuration.userContentController.add(handler, name: "testMessage")
view.loadHTMLString(html, baseURL: Bundle.main.bundleURL)
}
class ContentController: NSObject, WKScriptMessageHandler {
var parent: WKWebView?
init(_ parent: WKWebView?) {
self.parent = parent
}
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
print(message.body)
}
}
}
Configuration must be passed into the constructor of WKWebView. It can't be set after initialization.
struct JSWebView: NSViewRepresentable {
let html: String
func makeNSView(context: Context) -> WKWebView {
let preferences = WKPreferences()
preferences.javaScriptEnabled = true
let handler = MessageHandler()
let configuration = WKWebViewConfiguration()
configuration.userContentController.add(handler, name: "testMessage")
return WKWebView(frame: .zero, configuration: configuration)
}
func updateNSView(_ view: WKWebView, context: Context) {
view.loadHTMLString(html, baseURL: Bundle.main.bundleURL)
}
class MessageHandler: NSObject, WKScriptMessageHandler {
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
print(message.body)
}
}
}

callback javascript postMessage ios swift

Please tell me, there is an ios application, it contains WebKitView, and in it there is a site with this code:
<div id="test" style="height: 40px; width: 100px; background-color: powderblue;">Hello</div>
<script type="text/javascript">
document.getElementById("test").addEventListener("click", function () {
window.webkit.messageHandlers.test.postMessage("TEXT");
});
</script>
Application Code:
import UIKit
import WebKit
class ViewController: UIViewController, WKUIDelegate, WKNavigationDelegate, WKScriptMessageHandler {
var webView: WKWebView!
override func loadView() {
webView = WKWebView()
webView.navigationDelegate = self
view = webView
}
override func viewDidLoad() {
super.viewDidLoad()
let url = URL(string: "https://site")!
webView.load(URLRequest(url: url))
let config: WKWebViewConfiguration = WKWebViewConfiguration()
config.userContentController.add(self, name: "test")
webView = WKWebView(frame: self.view.frame, configuration: config)
webView?.navigationDelegate = self
self.webView?.load(URLRequest(url:url))
}
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
if message.name == "test", let messageBody = message.body as? String {
print(messageBody)
}
}
}
When you click on the div in xcode, the following error is displayed in the log
[general] Connection to daemon was invalidated
What could be the problem?
Sorry for my English.
I've added the shared HTML & javascript code in the main bundle as "example.html" and made changes in your code. It is working fine at my end.
override func viewDidLoad() {
super.viewDidLoad()
//Url from the html
let url = URL(fileURLWithPath: Bundle.main.path(forResource: "example", ofType: "HTML") ?? "")
// Configuring WKEWebview
let config: WKWebViewConfiguration = WKWebViewConfiguration()
config.userContentController.add(self, name: "test")
webView = WKWebView(frame: self.view.frame, configuration: config)
webView?.navigationDelegate = self
//Constraints related changes
webView.translatesAutoresizingMaskIntoConstraints = true
webView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
self.view.addSubview(self.webView)
// Load file
self.webView?.loadFileURL(url, allowingReadAccessTo: Bundle.main.bundleURL)
}
You can load your webpage using
self.webView.load(URLRequest(url: "https://www.site"))
Try it and let me know if you face any issue.

How to receive callback in ios from javascript?

I am trying to call native function in swift from wkwebview. This is what I have done so far:
Swift Part
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
contentController.add(self, name: "backHomePage")
config.userContentController = contentController
self.webView = WKWebView(frame: self.containerView.bounds, configuration: config)
webView.navigationDelegate = self
self.containerView.addSubview(self.webView)
if let url = URL(string: assessmentLink) {
webView.load(URLRequest(url: url))
}
}
And
extension WebFormVC: WKScriptMessageHandler {
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
print("javascript sending \(message.name), body: \(message.body)")
}
}
Javascript
function backHomePage(message) {
window.webkit.messageHandlers.backHomePage.postMessage(message);
}
where message can be any string for example: "success"
I am not currently receiving call back in userContentController didReceive method
UPDATE
I also tried sending data as key value for example window.webkit.messageHandlers.backHomePage.postMessage({"message" :"Javascript to swift!"});
, and it still didn't work.
I've tried to reproduce your case and everything seems to work as expected
import UIKit
import WebKit
class ViewController: UIViewController, WKScriptMessageHandler {
let content = """
<!DOCTYPE html><html><body>
<button onclick="onClick()">Click me</button>
<script>
function onClick() {
window.webkit.messageHandlers.backHomePage.postMessage("success");
}
</script>
</body></html>
"""
override func viewDidLoad() {
super.viewDidLoad()
let config = WKWebViewConfiguration()
config.userContentController = WKUserContentController()
config.userContentController.add(self, name: "backHomePage")
let webView = WKWebView(frame: CGRect(x: 0, y: 0, width: 200, height: 200), configuration: config)
view.addSubview(webView)
webView.loadHTMLString(content, baseURL: nil)
}
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
print(message.body)
}
}
Okay so the issue in my case was the call to window.webkit.messageHandlers.backHomePage.postMessage(message); was inside the .js file which was loaded externally and I dont really know why it was not being called.
I solved this issue by injecting an overload of the javascript function, keeping the body same as it is in that external .js file, and injected it at the end of the Document.
Thanks to #Andy for suggesting the idea of injecting userScript in the document. This information might come handy if anyone else faces same issue but is not the answer to the question "How to receive callback in ios from javascript?" so #Andy's answer is accepted since it precisely answers the question.

How to return boolean to javascript function from swift using wkwebview

This is my function in android :
#JavascriptInterface
public boolean GetMobileVersion() {
return true;
}
This is Calling function in JavaScript :
$(window).load(function () {
IsCallByMobileApp = false;
try {
IsCallByMobileApp = app.GetMobileVersion();/*Is call by android app*/
} catch (e) {
IsCallByMobileApp = false;
}
}
In iOS I am try to achieve same approach using WKWebView like this but it's not working:
func userContentController(_ userContentController: WKUserContentController,
didReceive message: WKScriptMessage) {
if(message.name == callbackhandler) {
webView.evaluateJavaScript("GetMobileVersion();") { (true, error) in
guard error == nil else {
print("there was an error")
return
}
print(Bool(true))
}
}
}
Can anyone tell me How to send true to Javascript function? Please give me answer in detail because I am new to iOS and Swift.
I have created a GitHub project for this. https://github.com/BKRApps/WKWebView-JS. check it out for more details.
Update the JavaScript:
function getMobileVersion(){
webkit.messageHandlers.VersionHandler.postMessage({})
}
function receivedMobileVersion(mobileVersion){
//here you will be getting the mobile version. Then execute the logic.
// i have added this only to cross check the version. you don't need to add this.
if(mobileVersion === true) {
webkit.messageHandlers.VerifyHandler.postMessage({version:mobileVersion})
}
}
getMobileVersion()
Add the below code to WKWebView Configuration:
configuration.userContentController.add(self, name: "VersionHandler")
// i have added this only to cross check the version. you don't need to add this.
configuration.userContentController.add(self, name: "VerifyHandler")
Update the WKScriptMessageHandler delegate:
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
switch message.name {
case "VersionHandler":
let mobileVersion = true //write the version logic and send the true or false.
let sendMobileVersionScript = "receivedMobileVersion(\(mobileVersion))"
self.wkWebView?.evaluateJavaScript("\(sendMobileVersionScript)", completionHandler: { (any, error) in
print("hello")
})
case "VerifyHandler":
print(message.body) // i have added this only to cross check the version. you
default:
break;
}
}
for more info : http://igomobile.de/2017/03/06/wkwebview-return-a-value-from-native-code-to-javascript/
Not sure if with WKWebView is the same as webview but you could try with plaintext as following:
func userContentController(_ userContentController: WKUserContentController,
didReceive message: WKScriptMessage) {
if(message.name == callbackhandler) {
webView.evaluateJavaScript("GetMobileVersion(true);") { (true, error) in
guard error == nil else {
print("there was an error")
return
}
print(Bool(true))
}
}
}

Calling Swift from JavaScript

I am new to Swift/iOS and have been working on an app that calls Swift code from JavaScript. There are not as many tutorials online and a lot of them are from when Swift was still in beta. Anyway, I was getting an error on my code and I am unable to compile it and I was wondering if any one had any tips and best practices when it comes to calling swift code from JavaScript.
Here is my code
import UIKit
import WebKit
class ViewController: UIViewController, WKScriptMessageHandler {
#IBOutlet var containerView : UIView! = nil
var webView: WKWebView?
override func loadView() {
super.loadView()
var contentController = WKUserContentController();
var userScript = WKUserScript(
source: "redHeader()",
injectionTime: WKUserScriptInjectionTime.AtDocumentEnd,
forMainFrameOnly: true
)
contentController.addUserScript(userScript)
contentController.addScriptMessageHandler(
self,
name: "callbackHandler"
)
var config = WKWebViewConfiguration()
config.userContentController = contentController
self.webView = WKWebView(frame: self.view.frame, configuration: config)
self.view = self.webView!
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
var url = NSURL(fileURLWithPath: NSBundle.mainBundle().pathForResource("index", ofType: "html")!)
var req = NSURLRequest(URL: url!)
self.webView!.loadRequest(req)
}
func userContentController(userContentController: WKUserContentController!,didReceiveScriptMessage message: WKScriptMessage!) {
if(message.name == "callbackHandler") {
println("JavaScript is sending a message \(message.body)")
} }
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
The error is on class ViewController: UIViewController, WKScriptMessageHandler { and it says
Type 'ViewController' does not conform to protocol 'WKScriptMessageHandler'.
Any help would be greatly appreciated.
I note that your method userContentController is:
func userContentController(userContentController: WKUserContentController!,
didReceiveScriptMessage message: WKScriptMessage!)
it should be:
func userContentController(userContentController: WKUserContentController,
didReceiveScriptMessage message: WKScriptMessage)
(no optionals ! in prototype).
If you need more help, please post your index.html source file

Categories

Resources