Javascript call to Swift from UIWebView
Asked Answered
S

3

10

I am trying to make a call from a javascript function in a UIWebView to Swift in iOS 10. I have setup a very basic project just to try and get this working, the code is below.

import UIKit

class ViewController: UIViewController, UIWebViewDelegate  {    
  @IBOutlet var webView: UIWebView!

    override func viewDidLoad() {
        super.viewDidLoad()

        let url = Bundle.main.url(forResource: "products", withExtension: "html")
        let request = NSURLRequest(url: url! as URL)
        webView.loadRequest(request as URLRequest)
    }

    @IBAction func closeDocumentViewer() {
        displayView.isHidden = true;
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }
}

If I just one to receive a string from a javascript function what would I have to add to the above?

Spectrogram answered 23/11, 2016 at 10:1 Comment(0)
R
3

You must a custom URL Scheme such as myawesomeapp and intercept requests to it using:

func webView(webView: UIWebView, shouldStartLoadWithRequest request: NSURLRequest, navigationType: UIWebViewNavigationType) -> Bool

Fire a call to native code using window.location=myawesomeapp://hello=world, and get the query params you pass from request.URL.query in the native code.

For more information, see my question about UIWebViews here: JavaScript synchronous native communication to WKWebView

Richert answered 23/11, 2016 at 17:5 Comment(1)
how do u get the params in the swift?Nano
S
20

I would suggest looking into using WKWebView instead UIWebView. You then won't need to register custom URL scheme. Additionally, UIWebView is obsolete and WKWebView has a lot of advantages, most notably performance and rendering as it runs in a separate process.

Link to Apple documentation and recommendation to use WKWebView https://developer.apple.com/reference/webkit/wkwebview/

Important

Starting in iOS 8.0 and OS X 10.10, use WKWebView to add web content to your > app. Do not use UIWebView or WebView.

That said, it's very simple to setup a native to javascript bridge:

import WebKit

final class ViewController: UIViewController, WKScriptMessageHandler {
    private var webView: WKWebView?
    override func loadView() {
        super.loadView()
        
        let webView = WKWebView(frame: self.view.frame)
        webView.configuration.userContentController.add(self, name: "scriptHandler")
        self.webView = webView
        
        self.view.addSubview(webView)
    }
    
    public func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
        print("Message received: \(message.name) with body: \(message.body)")
    }
    // rest of code
}

Then in your javascript code, call it:

window.webkit.messageHandlers["scriptHandler"].postMessage("hello");

I have written a library that leverages this and adds some fancy javascript syntax. https://github.com/tmarkovski/BridgeCommander

To use it, just reference the project (or add the swift and javascript files to your Xcode project) and call

webView = WKWebView(frame: self.view.frame)
let commander = SwiftBridgeCommander(webView!)

commander.add("echo") {
    command in
    command.send(args: "You said: \(command.args)")
}

You then will be able to use callback syntax in javascript like this

var commander = new SwiftBridgeCommander();
commander.call("echo", "Hello", function(args) {
        // success callback
    }, function(error) { 
        // error callback
});
Sonyasoo answered 2/12, 2016 at 14:22 Comment(5)
i was using wkwebview, but wkwebview can lead to dead ends. Like it is not syncing cookie with nshttpcookiestroragePermittivity
Yup, that's one downside of WKWebView. It runs the web engine in isolated process and has no access to your runtime memory.Sonyasoo
Check the WKWebsiteDataStore documentation for cookie management with WKWebView - developer.apple.com/documentation/webkit/wkwebsitedatastoreSonyasoo
It doesn't expose any method to access cookie prior to iOS 11Permittivity
Much appreciated @Dr.KameleonSonyasoo
R
3

You must a custom URL Scheme such as myawesomeapp and intercept requests to it using:

func webView(webView: UIWebView, shouldStartLoadWithRequest request: NSURLRequest, navigationType: UIWebViewNavigationType) -> Bool

Fire a call to native code using window.location=myawesomeapp://hello=world, and get the query params you pass from request.URL.query in the native code.

For more information, see my question about UIWebViews here: JavaScript synchronous native communication to WKWebView

Richert answered 23/11, 2016 at 17:5 Comment(1)
how do u get the params in the swift?Nano
S
0

We can call the swift function from Javascript with the help of WKScriptMessageHandler

A class conforming to the WKScriptMessageHandler protocol provides a method for receiving messages from JavaScript running on a webpage.

we need to add a listener to the event into our WKUserContentController

 let contentController = WKUserContentController()
    contentController.add(self, name: "loginAction”)

We have to implement its userContentController to receive the content sent from Javascript. for ex. limiting only to the "logout" we want for now.

  func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {       
       if message.name == "logout" {
           print("JavaScript is sending a message \(message.body)")
       }
   }

And from JavaScript end, They have to implement like this:

 function sendLogoutAction() {
       try {
           webkit.messageHandlers.logout.postMessage("logout");
       } catch(err) {
           console.log('The native context does not exist yet');
       }
    }
Selfassured answered 26/11, 2019 at 15:16 Comment(1)
I think that in your case didReceive message will never be triggered since your "keys" logout(JS side) and loginAction(Swift) do not match. Correct me if I am wrong.Stinkwood

© 2022 - 2024 — McMap. All rights reserved.