Pitfalls in WKWebView (Part 1)

WKWebView is the most flexible and modern framework of the iOS web engine. So if you need to show web sites, interact with these, customize the UI or the web sites etc, WKWebView is the way to go.

Even though WKWebView has a rich feature set and provides almost everything you would ever need, it still has some ugly bugs and shortcomings which can be very frustrating. Therefore I’ll start a small series of articles where I describe the issues I stumbled over and provide some hints how to avoid these pitfalls.

Bugs in the API documentation

Some of the problems are simply caused by a faulty API documentation. Which means the definition in the header files or public documentation does not reflect the reality. This is what the first part of the series is about.

upgradeKnownHostsToHTTPS

The class WKWebViewConfiguration defines the property upgradeKnownHostsToHTTPS, which according to the API definition should be available since iOS 14.5. But using the property under 14.5 will crash. The property seems to be available only since iOS 15, so if you want to use the property, make sure that you use it under iOS 15 and later only.

WKNavigationAction

WKWebView will call the delegate method

func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, preferences: WKWebpagePreferences, decisionHandler: @escaping ((WKNavigationActionPolicy, WKWebpagePreferences) -> Void))

whenever any frame loads a document to allow or block the page load. The parameter navigationAction holds all the details about the request. This WKNavigationAction object has a property sourceFrame which returns a WKFrameInfo object and the property request which holds the URLRequest to get the data from the server. All of these properties are not declared as Optional, so per definition they can be never nil. However there are circumstances where WKWebView nevertheless sets these properties to nil. When still using Objective C, this is not a big deal, however when using Swift, it is. While Objective C allows to call methods on a nil object, Swift will crash. While Objective C always allows to check for nil, Swift does not (in case the property is not defined as optional). This means whenever the WKWebView decides to return nil for these properties, your App will crash, and there’s nothing you can do here (almost…).

The sourceFrame is nil whenever a link creates a new window (via

func webView(_ webView: WKWebView, createWebViewWith configuration: WKWebViewConfiguration, for navigationAction: WKNavigationAction, windowFeatures: WKWindowFeatures) -> WKWebView? 

of the WKUIDelegate) and therefore the new instance of WKWebView does not have a sourceFrame yet.

To workaround these bugs of the WKWebView API, I’d suggest to implement the following extensions for WKNavigationAction and WKFrameInfo. Instead of using the original properties, just use the replacements from these extensions which return optionals and therefore allow to prevent the crashes.

extension WKNavigationAction { 
   
   public var sourceFrameInfo: WKFrameInfo? {
      return self.perform(#selector(getter: sourceFrame))?.takeUnretainedValue() as? WKFrameInfo
   }

   var urlRequest: URLRequest? {
      return self.perform(#selector(getter: request))?.takeUnretainedValue() as? URLRequest
   }

}
extension WKFrameInfo {

   var urlRequest: URLRequest? {
      return self.perform(#selector(getter: request))?.takeUnretainedValue() as? URLRequest
   }

}

Lockdown Mode support of WKWebView

Extremely weird is the API for the iOS lockdown mode. WKUIDelegate includes a delegate method which is called when the lockdown mode warning panel should be shown to the user.

@available(iOS 13.0, *)
optional func webView(_ webView: WKWebView, showLockdownModeFirstUseMessage message: String, completionHandler: @escaping @MainActor @Sendable (WKDialogResult) -> Void)

The delegate method is defined to be available since iOS 13. However if you actually implement the method, XCode will insist that this method is only available since iOS 15 (or the deployment target of your project). If you add an @available directive to limit your implementation to iOS 15 and later, you’ll get an error message that WKDialogResult is only available since iOS 16. If you change the @available directive to iOS 16, then you get an error that the method itself requires to be available since iOS 15.

So regardless how the @available directive is setup, there’s no way XCode will accept your implementation.

So right now, do not try to implement this delegate method.