This is a bit of a retrospective, but we have just been bitten terribly by the bug/change. We read a million articles / this forum / stackoverflow questions that all seemed to stem from cookie behavior changing in 11.3/11.4/12+, I've read this forum dozens of times. Our app got hammered by negative reviews, as users continuously kept getting logged out as the cookies stopped working consistently, and users were tweeting and calling our support center. Our QA process could not reproduce what was going on, we haven't pushed an App Store update to our app in months. Sometimes it worked, sometimes force closing worked, sometimes when you logout/login-as-another-user, then force close, and re-enter the app, you were logged in as the previous user, chaos...
Tangent: I'd like to complain that in iOS that there isn't just one best "WebView" class. It got splintered into UIWebView, and WkWebView, and app developers had to deal with intricacies related to managing networking, and cookies with each implementation. (Android did have a parallel chrome webview, but it got merged into just WebView, and networking and cookies are all managed by the OS, nobody had to carry an albatross of code managing which-webview-on-which-version-and-how-to-migrate). Since we want to support users with as many versions of iOS, we chose to build a WebViewAbstraction that allowed us to use the best WebView implementation per OS version. We then also synced cookies back to UserDefaults so that in case the user upgrades iOS versions, we would have a way of reading and migrating cookies. We then had to overload cookiesDidChange, we had to manage our cookie stores. And this rube-goldberg machine worked until iOS 11.3/11.4/12.0. Without any big announcement, that I'm aware of, very subtley, users who upgrade iOS, get random logout/cookie issues. Impossible to consistently reproduce.
Here's some psuedo-code of what our app used to be:
#onStart
if(iOS >= 11) {
webviewWrapper = WKWebView()
readCookiesFromUserDefaultsIntoWKWebView()
} else {
webviewWrapper = UIWebView()
readCookiesfromUserDefaultsIntoUIWebView()
}
// Our cookies expire after an hour, and the app-server sends updated cookies, by consuming a token
cookiesDidChange() {
syncCookiesFromWebViewToUserDefaults()
}
#onStop
saveCookiesFromWebviewToUserDefaults()
We also have HTTPS requests that our app has to make, and we have to wire it up with the correct session/cookieStore for which webview we're using. And when those HTTP requests complete, they then have to write new cookies into the cookieStore.
Our Solution:
- Drop support for iOS < 11. The WkWebView has presentation bugs in iOS < 11. The UIWebView has presentation bugs galore, and doesn't work well with our HTML app. And this whole debacle made clear that managing our own cookies is frought with adversity. We then removed the syncing to/fro UserDefaults. We removed UIWebView, we removed the WebViewWrapper, we removed our CookieStoreAbstractor. Very vanilla WkWebView inside our ViewController. We don't loadCookies, we don't setCookies, we don't cookiesDidChange, none of it.
When our HTTP requests have to read current cookies I manually construct a http header "cookieOne=1; cookieTwo=2". When the HTTP request responses have something to write to cookies, thats the only piece of magic we have:
let allHeaderFields = httpResponse.allHeaderFields as! [String : String]
let cookies = HTTPCookie.cookies(withResponseHeaderFields: allHeaderFields, for: URL(string: self.configuration.environment.cookieDomain)!)
// Need to run writing cookies on the "main thread". Load homepage when all writes are done (enter/leave/notify).
DispatchQueue.main.async {
let dispatchGroup = DispatchGroup()
for cookie in cookies {
dispatchGroup.enter()
webView.wkWebView!.configuration.websiteDataStore.httpCookieStore.setCookie(cookie, completionHandler: {
dispatchGroup.leave()
})
}
dispatchGroup.notify(queue: .main) {
// load the home page into the web view.
webView.loadURL(urlRequest: URLRequest(url: URL(string: self.configuration.environment.mainURL)!))
}
}
TL;DR; Custom cobbling a cookie solution is broken on iOS 11.3/11.4+, so we fixed it by removing everything, and just using WkWebView default, and our cookies aren't broken any more...
Its been a terrible couple of weeks of working all day and all night trying to find out what went wrong, where are the race conditions, and exploring every possible solution, until we just looked at what would be the simplest solution, and it involved nuking everything. I hope this post helps the next person.