WKWebview - Complex communication between Javascript & native code

后端 未结 7 2062
渐次进展
渐次进展 2020-12-12 17:39

In WKWebView we can call ObjectiveC/swift code using webkit message handlers eg: webkit.messageHandlers..pushMessage(message)

It works we

相关标签:
7条回答
  • 2020-12-12 18:15

    I managed to solve this problem - to achieve two-way communication between the native app and the WebView (JS) - using postMessage in the JS and evaluateJavaScript in the Native code.

    The solution from high-level was:

    • WebView (JS) code:
      • Create a general function to get data from Native (I called it getDataFromNative for Native, which calls another callback function (I called it callbackForNative), which can be reassigned
      • When wanting to call Native with some data and requiring a response, do the following:
        • Reassign callbackForNative to whatever function you want
        • Call Native from the WebView using postMessage
    • Native code:
      • Use the userContentController to listen to incoming messages from the WebView (JS)
      • Use evaluateJavaScript to call your getDataFromNative JS function with whatever params you want

    Here is the code:

    JS:

    // Function to get data from Native
    window.getDataFromNative = function(data) {
        window.callbackForNative(data)
    }
    
    // Empty callback function, which can be reassigned later
    window.callbackForNative = function(data) {}
    
    // Somewhere in your code where you want to send data to the native app and have it call a JS callback with some data:
    window.callbackForNative = function(data) {
        // Do your stuff here with the data returned from the native app
    }
    webkit.messageHandlers.YOUR_NATIVE_METHOD_NAME.postMessage({ someProp: 'some value' })
    

    Native (Swift):

    // Call this function from `viewDidLoad()`
    private func setupWebView() {
        let contentController = WKUserContentController()
        contentController.add(self, name: "YOUR_NATIVE_METHOD_NAME")
        // You can add more methods here, e.g.
        // contentController.add(self, name: "onComplete")
    
        let config = WKWebViewConfiguration()
        config.userContentController = contentController
        self.webView = WKWebView(frame: self.view.bounds, configuration: config)
    }
    
    func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
        print("Received message from JS")
    
        if message.name == "YOUR_NATIVE_METHOD_NAME" {
            print("Message from webView: \(message.body)")
            sendToJavaScript(params: [
                "foo": "bar"
            ])
        }
    
        // You can add more handlers here, e.g.
        // if message.name == "onComplete" {
        //     print("Message from webView from onComplete: \(message.body)")
        // }
    }
    
    func sendToJavaScript(params: JSONDictionary) {
        print("Sending data back to JS")
        let paramsAsString = asString(jsonDictionary: params)
        self.webView.evaluateJavaScript("getDataFromNative(\(paramsAsString))", completionHandler: nil)
    }
    
    func asString(jsonDictionary: JSONDictionary) -> String {
        do {
            let data = try JSONSerialization.data(withJSONObject: jsonDictionary, options: .prettyPrinted)
            return String(data: data, encoding: String.Encoding.utf8) ?? ""
        } catch {
            return ""
        }
    }
    

    P.S. I'm a Front-end Developer, so I'm very skilled in JS, but have very little experience in Swift.

    P.S.2 Make sure your WebView is not cached, or you might get frustrated when the WebView doesn't change despite changes to the HTML/CSS/JS.

    References:

    This guide helped me a lot: https://medium.com/@JillevdWeerd/creating-links-between-wkwebview-and-native-code-8e998889b503

    0 讨论(0)
提交回复
热议问题