Search code examples
viewswiftuiuiviewrepresentable

Start / update View in userContentController - Function


I try to start a second view with the help of the WKWebview by clicking a button. It also works. I get a callback in the function userContentController. How is it possible to start a new view in the app from this function.

struct MyWebView: UIViewRepresentable {

    var webPageURL = "https://google.de"

    func updateUIView(_ uiView: WKWebView, context: Context) {
        let myURL = URL(string:webPageURL)
        let myRequest = URLRequest(url: myURL!)
        uiView.load(myRequest)
    }

    func makeUIView(context: Context) -> WKWebView {

        let webConfiguration = WKWebViewConfiguration()
        let wkcontentController = WKUserContentController()

        wkcontentController.add(context.coordinator, name: "doStuffMessageHandler")
        webConfiguration.userContentController = wkcontentController

        let webView = WKWebView(frame: .zero,configuration: webConfiguration)
        context.coordinator.parent = webView // inject as weak

        return webView
    }

    func makeCoordinator() -> ContentController {
         ContentController() // let handler be a coordinator
     }
}

class ContentController: NSObject, WKScriptMessageHandler {
    weak var parent: WKWebView? // weak to avoid reference cycling


     func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage)
     {
        if message.name == "doStuffMessageHandler"
        {

            print(message.body)

        }
    }
}

struct MyWebView_Previews: PreviewProvider {
    static var previews: some View {
        MyWebView()
    }
}

Solution

  • The possible solution is using notifications (which allows keep those components independent).

    extension Notification.Name {
        static let didReceiveMessage = Notification.Name("didReceiveMessage")
    }
    
    class ContentController: NSObject, WKScriptMessageHandler {
        weak var parent: WKWebView? // weak to avoid reference cycling
    
         func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage)
         {
            if message.name == "doStuffMessageHandler"
            {
                NotificationCenter.default.post(name: .didReceiveMessage, object: message.body)
            }
        }
    }
    
    struct DemoHandleMessage: View {
        @State private var showMessage = false
        @State private var message: String? = nil
        var body: some View {
            VStack {
                if showMessage {
                     // switch back via bound flag to showMessage state
                    MessageView(text: message, dismiss: $showMessage)
                } else {
                    MyWebView()
                }
            }.onReceive(NotificationCenter.default.publisher(for: .didReceiveMessage)) { notification in
                self.message = notification.object as? String
                self.showMessage = true
            }
        }
    }