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?

3 Answers 3

21

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
});
Sign up to request clarification or add additional context in comments.

5 Comments

i was using wkwebview, but wkwebview can lead to dead ends. Like it is not syncing cookie with nshttpcookiestrorage
Yup, that's one downside of WKWebView. It runs the web engine in isolated process and has no access to your runtime memory.
Check the WKWebsiteDataStore documentation for cookie management with WKWebView - developer.apple.com/documentation/webkit/wkwebsitedatastore
It doesn't expose any method to access cookie prior to iOS 11
Much appreciated @Dr.Kameleon
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

1 Comment

how do u get the params in the swift?
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');
       }
    }

1 Comment

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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.