Search code examples
iosswiftuiwebviewwkwebview

WKWebView get Javascript Errors


Is there any way to get the JavaScript errors from the WebView? I mean the following errors and possible warnings?

JS error

I am running JS code with the following extension:

extension WKWebView {
    func evaluateJavaScriptWithReturn(_ javaScriptString: String) -> String? {
        var finished = false
        var jsValue: String?
        
        evaluateJavaScript(javaScriptString) { (result, error) in
            if error == nil {
                if result != nil {
                    jsValue = result as? String
                }
            } else {
                jsValue = nil
            }
            finished = true
        }
        
        while !finished {
            RunLoop.current.run(mode: .defaultRunLoopMode, before: Date.distantFuture)
        }
        
        return jsValue
    }
}

But error is not what I'm looking for! Is there any way to get in my app, the above errors? Thanks a lot!


Solution

  • To handle errors, we are going to add some JavaScript to the HTML we load into our page that will notify our native code about the error.

    First, you need to setup your web view with a script message handler:

    let controller = WKUserContentController()
    controller.add(self, name: "error")
    
    let configuration = WKWebViewConfiguration()
    configuration.userContentController = controller
    
    let webView = WKWebView(frame: .zero, configuration: configuration)
    

    I locally create the full HTML document and inject the following JavaScript:

    window.onerror = (msg, url, line, column, error) => {
      const message = {
        message: msg,
        url: url,
        line: line,
        column: column,
        error: JSON.stringify(error)
      }
    
      if (window.webkit) {
        window.webkit.messageHandlers.error.postMessage(message);
      } else {
        console.log("Error:", message);
      }
    };
    

    (I have the check and log since I sometime run the generated JavaScript in a browser.) You could also use the WKUserContentController to add a WKUserScript if you want to inject it into HTML you don’t fully control.

    Next when you load a string into your web view, be sure to use localhost as the base URL. If you don’t do this, all of your errors will be "Script error.".

    webView.loadHTMLString(html, baseURL: URL(string: "http://localhost/")!)
    

    Now define your WKScriptMessageHandler:

    func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
        switch message.name {
        case "error":
            // You should actually handle the error :)
            let error = (message.body as? [String: Any])?["message"] as? String ?? "unknown"
            assertionFailure("JavaScript error: \(error)")
        default:
            assertionFailure("Received invalid message: \(message.name)")
        }
    }
    

    You can add other cases to the switch statement for other messages you add to the WKUserContentController.