Arundale Ramanathan
Arundale Ramanathan

Reputation: 2069

Getting invokeUndefinedMethodFromWebScript to work in Swift

I am trying to get Javascript-Swift interface work with WKWebView and someone seems to have got it working here.

However I am unable to get it working in Swift (I am new to Swift and newer to Obj-C). I tried following code in ViewController.swift with WKWebView added to Main.storyboard and connected:

import WebKit

class ViewController: NSViewController, WKUIDelegate {
    @IBOutlet weak var wkWebView: WKWebView!
    override func viewDidLoad() {
        super.viewDidLoad()
        let url = URL(string: "http://127.0.0.1:8080")
        let request = URLRequest(url: url!)
        wkWebView!.configuration.preferences.javaScriptEnabled = true
        wkWebView!.load(request)
    }
}

extension WKWebView {
    open override func invokeUndefinedMethod(fromWebScript name: String!, withArguments arguments: [Any]!) -> Any! {
        return "Hello From Swift"
    }
}

and on HTML side:

<html>
  <body>
    <p>Hello World</p>
    <input type=text id='i'/>
    <input type=button value='Submit' onclick='document.getElementById(\"i\").value = window.external.MY_ObjectiveCFunction();'>
  </body>
</html>

But when I click on Submit button nothing happens. If I use document.getElementById("i") = "hello", it works though. @ctrlspace seems to have got it working somehow (See link above).

Any ideas?

Upvotes: 1

Views: 1094

Answers (1)

OOPer
OOPer

Reputation: 47896

You have found an old article which was written for an ancient WebView of macOS, not for WKWebView.

You use WKScriptMessageHandler to make interaction between JavaScript and Swift, when using WKWebView.

ViewController.swift:

import WebKit

class ViewController: NSViewController, WKUIDelegate {
    @IBOutlet weak var wkWebView: WKWebView!
    override func viewDidLoad() {
        super.viewDidLoad()
        let url = Bundle.main.url(forResource: "index", withExtension: "html")! //Simplified for testing
        let request = URLRequest(url: url)
        wkWebView.configuration.preferences.javaScriptEnabled = true
        wkWebView.configuration.userContentController.add(self, name: "mySwiftMessage") //<-
        wkWebView.load(request)
    }

    //...
}

extension ViewController: WKScriptMessageHandler {
    func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
        switch message.name {
        case "mySwiftMessage":
            if let callbackName = message.body as? String {
                message.webView?.evaluateJavaScript("\(callbackName)('Hello From Swift');", completionHandler: nil)
            }
        default:
            break
        }
    }
}

index.html:

<html>
    <body>
        <script>
            function myCallback(value) {
                document.getElementById("i").value = value;
            }
        </script>
        <p>Hello World</p>
        <input type=text id='i'/>
        <input type=button value='Submit' onclick='window.webkit.messageHandlers.mySwiftMessage.postMessage("myCallback");'>
    </body>
</html>

ADDITION

To make WKWebView work on prior iOS's than 11, you just need to instantiate it programmatically:

import WebKit

class ViewController: UIViewController, WKUIDelegate {

    var wkWebView: WKWebView! //<- NOT an IBOutlet

    override func viewDidLoad() {
        super.viewDidLoad()
        let webConfig = WKWebViewConfiguration()
        webConfig.preferences.javaScriptEnabled = true
        webConfig.userContentController.add(self, name: "mySwiftMessage")
        wkWebView = WKWebView(frame: self.view.bounds, configuration: webConfig)
        view.addSubview(wkWebView)
        //You may need to add some constraints...
        //And create a better `index.html` for iOS...
        let url = Bundle.main.url(forResource: "index", withExtension: "html")!
        let request = URLRequest(url: url)
        wkWebView.load(request)
    }

    //...
}

Upvotes: 1

Related Questions