Swift webview: How to call correctly swift code from javascript?

24,927

Solution 1

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!
}

Solution 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) {}
    }
}
Share:
24,927

Related videos on Youtube

adam
Author by

adam

Updated on December 22, 2020

Comments

  • adam
    adam over 3 years

    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.
        }
    
    
    }
    
  • Dev
    Dev over 8 years
    Please note calling self.view.frame in the loadView() method causes EXC_BAD_ACCESS crash.
  • Eduard
    Eduard over 8 years
    @Dev what should I call instead self.view.frame if it's casugin EXC_BAD_ACCESS
  • sandrom
    sandrom over 7 years
    @Dev it only does if you forget to call super.loadView() beforehand
  • Heitor
    Heitor about 5 years
    Hi, could you help me by providing the MacOS version of it?
  • Heitor
    Heitor about 5 years
    What about the MacOS version?