swiftwkwebviewswiftuiwkwebviewconfiguration

Argument Type does not conform to expected type 'WKScriptMessageHandler'


I'm trying two-way integration between Swift and Javascript interfacing with SwiftUI.
Here is interfacing WebKit with SwiftUI.

import SwiftUI
import WebKit

struct ggWebView : UIViewRepresentable {

    let filePath: String

    func makeUIView(context: Context) -> WKWebView  {
        return WKWebView()
    }

    func updateUIView(_ uiView: WKWebView, context: Context) {
        uiView.configuration.userContentController.add(self, name: "jsHandler")
        let bundleURL = Bundle.main.resourceURL!.absoluteURL
        let html = bundleURL.appendingPathComponent(filePath)
        uiView.loadFileURL(html, allowingReadAccessTo:bundleURL)
    }

    func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
        if message.name == "jsHandler"{
            print(message.body)
        }
    }
}

userContentController.add(self, name: "jsHandler") shows an error at self with Argument type 'ggWebView' does not conform to expected type 'WKScriptMessageHandler'.


Solution

  • As WKScriptMessageHandler require that object that implements it inherit from NSObject it is better to create a separate class ContentController that will be implementing those protocol, rather than changing the type of GgWebView.

    import SwiftUI
    import WebKit
    
    struct GgWebView: UIViewRepresentable {
    
        let filePath: String
        let contentController = ContentController()
    
        func makeUIView(context: Context) -> WKWebView  {
            return WKWebView()
        }
    
        func updateUIView(_ uiView: WKWebView, context: Context) {
            uiView.configuration.userContentController.add(contentController, name: "jsHandler")
            let bundleURL = Bundle.main.resourceURL!.absoluteURL
            let html = bundleURL.appendingPathComponent(filePath)
            uiView.loadFileURL(html, allowingReadAccessTo:bundleURL)
        }
    
        class ContentController: NSObject, WKScriptMessageHandler {
            func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
                if message.name == "jsHandler"{
                    print(message.body)
                }
            }
        }
    }
    

    You have to confirm ggWebView (BTW this is not the best name for a class because it should start with capital letter) to WKScriptMessageHandler protocol and implement func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) method. You have to change the type of ggWebView from structure to class. I can see that you already add a userContentController(_:didReceive:) method, so you only need to update your class signature to:

    class ggWebView: UIViewRepresentable, WKScriptMessageHandler {