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?

Tony Goodchild
  • 331
  • 1
  • 2
  • 13

3 Answers3

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

class ViewController: UIViewController, WKScriptMessageHandler {
   var webView: WKWebView?
    override func loadView() {
        super.loadView()
        
        webView = WKWebView(frame: self.view.frame)
        webView?.configuration.userContentController.add(self, name: "scriptHandler")
        
        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
});
Community
  • 1
  • 1
Tomislav Markovski
  • 12,331
  • 7
  • 50
  • 72
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

Community
  • 1
  • 1
paulvs
  • 11,963
  • 3
  • 41
  • 66
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');
       }
    }