Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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 run 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!

like image 469
Konstantinos Natsios Avatar asked May 08 '18 09:05

Konstantinos Natsios


People also ask

How do you evaluate JavaScript in WKWebView?

How to run JavaScript on a WKWebView with evaluateJavaScript() Using evaluateJavaScript() you can run any JavaScript in a WKWebView and read the result in Swift. This can be any JavaScript you want, which effectively means you can dig right into a page and pull out any kind of information you want.

How do I debug Safari WKWebView?

Debugging with WKWebViewOpen Safari and press ⌘+, to open Preference, under the Advanced tab, check “Show Develop menu in menu bar”. Not until we have the project build can we use the Safari debugger. The debugger is under Develop → Your simulator or device.

What is the difference between UIWebView and WKWebView?

Unlike UIWebView, which does not support server authentication challenges, WKWebView does. In practical terms, this means that when using WKWebView, you can enter site credentials for password-protected websites.

Is WKWebView secure?

The WKWebView is a modern API applying all the modern web security mechanisms, it's still maintained by Apple and gets updates. The good thing about WKWebView is that it does out-of-process rendering, so if the attackers find a memory corruption vulnerability in it, your application's process is still isolated.


2 Answers

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.

like image 162
Sam Soffes Avatar answered Oct 07 '22 06:10

Sam Soffes


Try the following way.

First, inject JS code to catch the error in html content:

window.onerror = function(error) {
  alert(error); // Fire when errors occur. Just a test, not always do this.
};

Second, display the error in WKWebView delegate method:

func webView(_ webView: WKWebView, runJavaScriptAlertPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping () -> Void) {
    let alertController = UIAlertController(title: "alert", message: message, preferredStyle: UIAlertControllerStyle.alert)
    alertController.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.cancel, handler: nil))
    self.present(alertController, animated: true, completion: nil)

    print("An error from web view: \(message)")
}

Remember to set delegate:

webView.uiDelegate = self

If you want to avoid the alerts, the alternative way is using decidePolicyForNavigationAction method to receive the JS's callback which is like window.open("error://message","_self")

like image 43
Yun CHEN Avatar answered Oct 07 '22 06:10

Yun CHEN