25

I'm trying to make my javascript interact with swift code but unfortunately i didn't succeed.

For the moment, i just tried to change the headers colors and display a message like you will see in the code below.

Here's my (index.html) code:

<!DOCTYPE html>
<html>
    <head>
        <title>Test</title>
        <meta charset="UTF-8">
    </head>
    <body>
        <h1>WebView Test</h1>
        <script type="text/javascript" src="main.js"></script>
    </body>
</html>

Here's my (main.js -JavaScript) code:

function callNativeApp () {
    try {
        webkit.messageHandlers.callbackHandler.postMessage("Send from JavaScript");
    } catch(err) {
        console.log('error');
    }
}

setTimeout(function () {
    callNativeApp();
}, 5000);

function redHeader() {
    document.querySelector('h1').style.color = "red";
}

Here's my (ViewController.swift) 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()
        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.
    }


}
1

2 Answers 2

17

You have everything set up properly, but you aren't giving your WKWebViewConfiguration instance to the WKWebView. Since the configuration has the details of the Javascript/Swift bridge, you can't talk back and forth.

override func loadView() {
    // ...
    var config = WKWebViewConfiguration()
    config.userContentController = contentController

    self.webView = WKWebView(frame: self.view.frame, configuration: config)
    self.view = self.webView!
}
Sign up to request clarification or add additional context in comments.

4 Comments

Please note calling self.view.frame in the loadView() method causes EXC_BAD_ACCESS crash.
@Dev what should I call instead self.view.frame if it's casugin EXC_BAD_ACCESS
@Dev it only does if you forget to call super.loadView() beforehand
What about the MacOS version?
2

my 2 cents, using a javascript callback with JSON ... for full class definition and layout, refer to adam's code

import UIKit
import WebKit

class ViewController: UIViewController, WKScriptMessageHandler {
    var webView: WKWebView?
    ...

then

override func loadView() {
    let theConfiguration = WKWebViewConfiguration()
    let contentController = theConfiguration.userContentController

    // alert fix, at start to allow a JS script to overwrite it
    contentController.addUserScript( WKUserScript(
        source: "window.alert = function(message){window.webkit.messageHandlers.messageBox.postMessage({message:message});};",
        injectionTime: WKUserScriptInjectionTime.AtDocumentStart,
        forMainFrameOnly: true
    ) )
    contentController.addScriptMessageHandler(self, name: "messageBox")

    self.webView = WKWebView(frame: self.view.frame, configuration: theConfiguration)

    // and here things like: self.webView!.navigationDelegate = self
    self.view = self.webView!  // fill controllers view
}

and specifically

func userContentController(userContentController: WKUserContentController, didReceiveScriptMessage message: WKScriptMessage) {

    if message.name == "messageBox" {
        let sentData = message.body as! Dictionary<String, String>

        let message:String? = sentData["message"]

        let alertController = UIAlertController(title: nil, message: message, preferredStyle: UIAlertControllerStyle.Alert)
        alertController.addAction(UIAlertAction(title: NSLocalizedString("OK", comment:"btnOK"), style: .Default, handler: nil))
        self.presentViewController(alertController, animated: true) {}
    }
}

1 Comment

Hi, could you help me by providing the MacOS version of it?

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.