javascriptswiftuiwkwebview

How to Listen to JavaScript CallBack from SwiftUI?


I am new in SwiftUI I have created WebView like this in SwiftUI

struct WebView: UIViewRepresentable {
    @Binding var title: String
    var url: URL
    var loadStatusChanged: ((Bool, Error?) -> Void)? = nil

    func makeCoordinator() -> WebView.Coordinator {
        Coordinator(self)
    }

    func makeUIView(context: Context) -> WKWebView {
        let view = WKWebView()
        view.navigationDelegate = context.coordinator
        view.load(URLRequest(url: url))
        return view
    }

    func updateUIView(_ uiView: WKWebView, context: Context) {
        // you can access environment via context.environment here
        // Note that this method will be called A LOT
    }

    func onLoadStatusChanged(perform: ((Bool, Error?) -> Void)?) -> some View {
        var copy = self
        copy.loadStatusChanged = perform
        return copy
    }

    class Coordinator: NSObject, WKNavigationDelegate,WKScriptMessageHandler {
        @State private var studentAppID = UserDefaults.standard.string(forKey: "studentAppID")
        let parent: WebView

        init(_ parent: WebView) {
            self.parent = parent
        }

        func webView(_ webView: WKWebView, didCommit navigation: WKNavigation!) {
            print(webView.url as Any)
            parent.loadStatusChanged?(true, nil)
        }

        func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
            print(webView as Any)
            print(webView.url as Any)
            parent.title = webView.title ?? ""
            webView.evaluateJavaScript("document.getElementsByName('applnID')[0].value='\(studentAppID!)'", completionHandler: nil)
            parent.loadStatusChanged?(false, nil)
        }

        func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) {
            parent.loadStatusChanged?(false, error)
        }
        
        func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
//            guard let dict = message.body as? [String : AnyObject] else {
//                return
//            }
            
            print(message.body)
        }

    }
}

I want to configure function so that I can received response from WebView when user click on the button tag on the WebView
I found the code for this in swift but don't understand how can I used it in SwiftUI The code is like this

class ViewController: UIViewController, WKScriptMessageHandler {
  let content = """
  <!DOCTYPE html><html><body>
  <button onclick="onClick()">Click me</button>
  <script>
  function onClick() {
    window.webkit.messageHandlers.backHomePage.postMessage("success");
  }
  </script>
  </body></html>
  """

  override func viewDidLoad() {
    super.viewDidLoad()

    let config = WKWebViewConfiguration()
    config.userContentController = WKUserContentController()
    config.userContentController.add(self, name: "backHomePage")

    let webView = WKWebView(frame: CGRect(x: 0, y: 0, width: 200, height: 200), configuration: config)

    view.addSubview(webView)

    webView.loadHTMLString(content, baseURL: nil)
  }

  func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
    print(message.body)
  }
}

as flow in this link

How to receive callback in ios from javascript?

Can I configure same in SwiftUI if yes then how?


Solution

  • What you have implemented is correct however you have missed out on WKWebViewConfiguration object.

    You need to create web view configuration that registers a message handler for your javascript code to call, see below:

    1. Create a class that can receive callbacks from JavaScript code:

      let scriptMessageHandler = ScriptMessageHandler()
      
      class ScriptMessageHandler: NSObject, WKScriptMessageHandler {
          func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
              print(message.body)
          }
      }
      
    2. Create a config object and pass an object of ScriptMessageHandler as handler.

      let config = WKWebViewConfiguration()
      config.userContentController = WKUserContentController()
      config.userContentController.add(scriptMessageHandler, name: "backHomePage")```
      

    You can do this in your makeUIView(context:) -> WKWebView method implementation, right before you create Web View object and pass this config to your web view.

    In Javascript, above config can then be called like below:

    function onClick() {
       window.webkit.messageHandlers.backHomePage.postMessage("success");
    }
    

    When above function is called from JavaScript, you will get a callback in your userContentController(_ didReceive:) implementation.