Up until iOS 8 (2014), displaying web content in an application required you to either launch out to Safari or use UIWebView. The benefit of using UIWebView was the the user did not have to leave your application, but the downside was that UIWebView was significantly underpowered when it came to Safari as it had (and still does) the benefit of a more modern rendering and JavaScript engine, so UIWebView content did not perform as well. This lead Apple to releasing the WKWebView API in iOS 8 to bring in-app web content up to par. However, this did not come without some growing pains as WKWebView couldn’t load files until iOS 9, didn’t get data detectors until iOS 10, and couldn’t handle custom URL schemes, be in storyboards, or have manual cookie management until iOS 11. To this day, it still doesn’t support NSURLProtocols or Mutual TLS. The biggest pain point though is that UIWebView and UIWebViewDelegate became two delegates and a bunch of other support classes, leading to implementation headaches due to the complexity. That being said, the performance and constant feature updates with minor iOS releases that pull in newer WebKit versions are very helpful to embedded web content (being open source is also a major plus), but, I’ve run into to some weird issues using WKWebView that I’d like to share.
Issue 1 - Data Stores
When leveraging embedded web content, usually you need to coordinate all of the data shared between the web views so that you can leverage the cache, cookie, and local storage in each. To do so, you have to create a psuedo-singleton WKWebViewConfiguration object with a shared WKProcessPool. Once you have that setup, all you need to do is initialize each instance of WKWebView with that base configuration object and you’re off to the races. Or at least, that’s what I thought. Turns out, there is one undocumented behavior that you need to be aware of if you use shared cookies. Apparently, when you set the configuration’s website data store, you need to use a non-persistent data store instead of the default for cookies to be immediately available after you add them. Otherwise, your web content will keep failing authentication until you reauthenticate and then magically, the cookie is available. I still have no idea why this is required since the documentation doesn’t state this, but in the WWDC demo, that is what Apple uses to achieve the desired behavior. ¯\(ツ)/¯
Issue 2 - Cookie Management
As I mentioned earlier, in iOS 11 WebKit now allows access to the cookies in the remote process. The API is pretty simple, call set or delete and then when the completion block runs, you’re supposed to be able to proceed with whatever you were going to do. However, this behavior is consistently inconsistent. I’ve noticed a couple of things when using this API to manage cookies: 1) calling set to replace an existing cookie with a new one with a different value works sometimes and 2) calling delete sometimes doesn’t actually delete the cookie. This has lead to all sorts of weird behavior since the cookie I’m managing is tied to the user’s authentication state. To workaround this, I’ve tried the removal methods on WKWebSiteDataStore to no avail (also, why is there not a delete all cookies method on WKHTTPCookieStore since it has a get all cookies method?). So, I’ve had to resort to resetting the data store on the shared configuration on logout to alleviate the cookie problem since it guarantees no cookie reuse. This does mean that the cache is invalidated, so there is a performance loss. I’m hoping that future iOS versions will make cookie management more reliable so that I don’t have to use the nuclear option for such a trivial use case.
Embedded web content has never truly been a priority for Apple since the native experience is so much better, but cross-platform and progressive web apps do exist and make money, so they might as well throw those developers a bone every now and then. If you can’t avoid web content in your apps, I’d recommend going down the SFSafariViewController path if possible since it is so easy to implement and Apple does all of the heavy lifting for you.