WKWebView - Handling Errors

Posted by Grego on January 6, 2017

Yesterday I got stuck trying to figure out why my WKNavigationDelegate was not picking up errors, today I found the answer.

In short, the Apple docs suck at explaining things.

Second to that, I was checking HTTP responses for errors. But if I don’t get a response, then there’s no status code… This I realized already but when I tried using some of the other error delegate methods, they were not being fired.

The answer came to me when I thought that maybe the web view was not timing out. I found an answer for how to add a time out to WKWebView and realized that the method that needs to be implemented is actually webView(_:, didFailProvisionalNavigation:, withError:):

From StackOverflow:

func webView(webView: WKWebView,
    didFailProvisionalNavigation navigation: WKNavigation!,
    withError error: NSError) {
  if error.code == -1001 { // TIMED OUT:
    // CODE to handle TIMEOUT
  } else if error.code == -1003 { // SERVER CANNOT BE FOUND
    // CODE to handle SERVER not found
  } else if error.code == -1100 { // URL NOT FOUND ON SERVER
    // CODE to handle URL not found
  }
}

Apple’s WKNavigationDelegate documentation describes this method:

Called when an error occurs while the web view is loading content.

Apple maintains a list of URL-related error codes. They don’t show the raw values when you are viewing Swift code samples. If you switch it to objective-c you’ll see the numbers also. However since magic numbers are greatly discouraged in either language, you would never need them.

In my particular case I don’t care what the specific error is, I just want to respond to all errors in the same fashion. The error codes could still be useful, for example, if you wanted to log the error or display specific messages for each error that make more sense.

How to Handle HTTP Responses

Since I alluded to this up top, I thought I’d throw in also how to handle http responses. Using webView(_:, decidePolicyFor:, decisionHandler:):

/**
 * Handles all HTTP responses, from WKNavigationDelegate
 */
func webView(_ webView: WKWebView,
    decidePolicyFor navigationResponse: WKNavigationResponse,
    decisionHandler: @escaping (WKNavigationResponsePolicy) -> Void) {

  guard let statusCode
      = (navigationResponse.response as? HTTPURLResponse)?.statusCode else {
    // if there's no http status code to act on, exit and allow navigation
    decisionHandler(.allow)
    return
  }

  if statusCode >= 400 {
    // error has occurred
  }

  decisionHandler(.allow)
}

The decisionHandler that gets passed in is used to intercept the response to tell whether or not to allow the navigation. I just cooked up something simple here but you may want to change that particular part of the behavior yourself.

In both cases I’m saying to allow this page to be navigated to because I don’t care either way about the result, I just want to be able to handle or log any bad statusCodes. For my use case the web view is not always visible.

Other WKWebView Functionality

I found another interesting article on WKWebView’s capabilities demonstrates other tricks you can do with web views. I will take a deeper look into this article at a later date and write more.