iOS 11.3: WKWebView cookie synchronizing stopped working

Hi,


Anyone else having issues with WKWebView cookies after iOS 11.3? (Or any recomondations of what we might be doing wrong?)


Working on an app with web content, we switched fro UIWebView to WKWebView after iOS 11.

We use URLSession for authentication, then webviews to show content.


In order to syncrhonzie cookies to WKWebViews, we have used the following procedure:


1. Log in with URLSession

2. Create a WKWebView with the cookies from the URLSession:

let config = WKWebViewConfiguration()
config.processPool = WKProcessPool()
let cookies = HTTPCookieStorage.shared.cookies ?? [HTTPCookie]()
cookies.forEach({ config.websiteDataStore.httpCookieStore.setCookie($0, completionHandler: nil) })
          
let wkWebView = WKWebView(frame: bounds, configuration: config)
...


3. Load the URL.


Worked like a charm, until iOS 11.3.


Something fishy is happening, I have checked that the HTTPCookieStorage contains the correct cookies, and verified that the completionHandler of "setCookie" gets called before the url loads. However, the cookies are not being set.


But. If we wait with creating the WKWebView/transfering cookies by about ~3 seconds after loggign in, it seems to work.


This might seem like a bug introduced in iOS 11.3 (?), or any other suggestions?

Post not yet marked as solved Up vote post of boedev Down vote post of boedev
39k views

Answers

boedev, I have exactly the same issue. Were you able to find any solution for that?

Did you tried to load WKWebView after cookies setting completion?
Try to create dispatch group and create/load WkWebView in group.notify handler

it doesn't work on iOS 11.3. I'm creating web view after cookies injection and with sharing the same Pool. On iOS 11.2.6 it works as expected...

Someone recently opened a DTS incident with a report of a similar problem, which gave me the opportunity to try it out. In my tests I saw the cookie show up on 11.3 just fine. So there’s something specific about your app that’s triggering this.

Did you try what dskibin suggested, namely, using a dispatch group to guarantee that your completion handlers are all called before you load up the web view?

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Thanks for jumping on this issue.

We are using RxSwift for async flow management instead of dispatch group. And I'm pretty sure that all injections happened before web view loaded.


A few interesting notes about this:

1) injection works well on both 11.2.6. and 11.3. I could see cookies in the httpCookieStore

2) on 11.2.6 cookies are attached to the web page as expected

3) on 11.3 WkWebView just ignore them

4) adding the delay in web view load for 3-10 seconds doesn't fix the issue

5) after the application start web view loaded without cookies but if I change user (try to set new cookies) then cookies become attachable.

eskimo, here is the code snippet with dispatch group that works nice on iOS 11.2.6 and stop working on 11.3


    override func viewDidLoad() {
        super.viewDidLoad()
     
        let pool = WKProcessPool()
        let configuration = WKWebViewConfiguration()
        configuration.processPool = pool
     
        let webView = WKWebView(frame: view.bounds, configuration: configuration)
        view.addSubview(webView)
     
        let sem = DispatchGroup()
        cookies.forEach { cookie in
            sem.enter()
            configuration.websiteDataStore.httpCookieStore.setCookie(cookie, completionHandler: {
                sem.leave()
            })
        }
     
        sem.notify(queue: .main) {
            let url = URL(string: "https://some-private-link.com")
            let request = URLRequest(url: url)
            webView.load(request)
        }
    }

    fileprivate var cookies: [HTTPCookie] {
       
        let isEnvSecured = true
       
        var emailCookieProperty: [HTTPCookiePropertyKey: Any] = [
            HTTPCookiePropertyKey.domain: "some-private-link.com",
            HTTPCookiePropertyKey.path: "/",
            HTTPCookiePropertyKey.name: "X-User-Email",
            HTTPCookiePropertyKey.value: "some@example.com"]
       
        if isEnvSecured {
            emailCookieProperty[HTTPCookiePropertyKey.secure] = "Yes" as NSString
        }
       
        var tokenCookieProperty: [HTTPCookiePropertyKey: Any] = [
            HTTPCookiePropertyKey.domain: "some-private-link.com",
            HTTPCookiePropertyKey.path: "/",
            HTTPCookiePropertyKey.name: "X-User-Token",
            HTTPCookiePropertyKey.value: "N9m6M7mT-tm9yFVDVoLP"]
       
        if isEnvSecured {
            tokenCookieProperty[HTTPCookiePropertyKey.secure] = "Yes" as NSString
        }
       
        let emailCookie: HTTPCookie! = HTTPCookie(properties: emailCookieProperty)
        let tokenCookie: HTTPCookie! = HTTPCookie(properties: tokenCookieProperty)
        return [emailCookie, tokenCookie]
    }


Do you see any potential issues with it? Could you please share your own code snippet so we could check that locally as well?

I am facing similar issue. I need to set an authentication cookie and the code works fine on iOS 11.2 I am able to see in Charles that the cookie is set and I am able to open the authenticated webpage in WKWebView but the same code doesnt work on iOS11.3 Even in charles I see that the cookie is not set. Below is what I am trying:


//Create webview
let webView = WKWebView(frame: .zero, configuration: WKWebViewConfiguration())
//Add webview to the UI
//....
//Set the cookie
let authCookie = HTTPCookie.init(properties: [HTTPCookiePropertyKey.name: "OAuth.AccessToken", HTTPCookiePropertyKey.value: "some_authToken", HTTPCookiePropertyKey.domain: ".somedomain.com", HTTPCookiePropertyKey.path: "/", HTTPCookiePropertyKey.secure: "TRUE", HTTPCookiePropertyKey.expires: Date.distantFuture])
if let reqCookie = authCookie {
     webView.configuration.websiteDataStore.httpCookieStore.setCookie(reqCookie) {
          DispatchQueue.main.async {
               self.webView.load(checkoutRequest)
          }
     }
}

As I mentioned back on 12 Apr, I tried this here in my office and it worked for me. I’m not sure why it’s failing in some cases and not others. If you’d like me to investigate your specific case, you should open a DTS tech support incident and we can pick things up in that context.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

I am facing the same issue on iOS 11.3. Same code works fine on 11.2.

The problem seems to be something with threads, if I add an observer to the cookie store with the following code:


// In viewDidLoad
let authCookie = HTTPCookie.init(properties: [HTTPCookiePropertyKey.name: "OAuth.AccessToken", HTTPCookiePropertyKey.value: "some_authToken", HTTPCookiePropertyKey.domain: ".somedomain.com", HTTPCookiePropertyKey.path: "/", HTTPCookiePropertyKey.secure: "TRUE", HTTPCookiePropertyKey.expires: Date.distantFuture])|
WKWebsiteDataStore.default().httpCookieStore.add(self)
WKWebsiteDataStore.default().httpCookieStore.setCookie(authCookie)

public func cookiesDidChange(in cookieStore: WKHTTPCookieStore) {
     print("Cookies did change")
     cookieStore.getAllCookies { (cookies) in
          print("Cookie exists?: \(cookies.contains(where: { $0.name == "OAuth.AccessToken" }))")
     }
     DispatchQueue.main.async {
          cookieStore.getAllCookies { (cookies) in
               print("Cookie exists on main thread?: \(cookies.contains(where: { $0.name == "OAuth.AccessToken" }))")
          }
     }
}


Then the output is:


Cookies did change
Cookie exists?: true
Cookie exists on main thread?: false


As the webview always loads requests on the main thread, it doesn't see the cookie in the storage so it never gets sent.

I also have the same problem.

@Soumin and @nRewik, my offer from 30 Apr is still open.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Hey @eskimo and others: We had the same problem here, and I believe I've found the true cause and solution. This change (https://github.com/WebKit/webkit/commit/d9d6e5c82c4a74cd573f3f119f166ffcee477b04) caused WebKit to prevent initializing the WKWebsiteDataStore until necessary. We have a flow in our app where, if the webview attempts to navigate to a particular URL that requires a cookie, we cancel the navigation, download and set a cookie, and in the cookie setter's completion handler, load the original request (this time with the cookie).


However, since we've canceled the navigation, the WKWebView doesn't see the creation of the data store "necessary" and queues our completion handler. The URL is never loaded, since our completion handler is never called. It's almost like a deadlock.


Since this isn't the behavior I'd expect, I filed this as rdar://40100673 and https://bugs.webkit.org/show_bug.cgi?id=185483.


Here's our problematic code, and a workaround, which forces the creation of the WKWebsiteDataStore:

    if (@available(iOS 11.0, *)) {
        [webView.configuration.websiteDataStore.httpCookieStore setCookie:cookie
                                                        completionHandler:^{
            [webView loadRequest:request];
        }];

        // WORKAROUND: Force the creation of the datastore by calling a method on it.
        [webView.configuration.websiteDataStore fetchDataRecordsOfTypes:[NSSet<NSString *> setWithObject:WKWebsiteDataTypeCookies]
                                                      completionHandler:^(NSArray<WKWebsiteDataRecord *> *records) {}];
    }

Thanks for the heads up. I now have a couple of folks who’ve opened incidents about this. We haven’t yet got to the stage where I can tell why their apps are failing while my test app works, but I’ll keep your scenario in mind.

Since this isn't the behavior I'd expect, I filed this as rdar://40100673 …

And thanks for that too.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

I tired to remove the cookies before loading the webview like this. It works for me. But I do not want to remove the cookies everytime I load the webview. So its workaround not a solution.


            let group = DispatchGroup()
            group.enter()
            webView.configuration.websiteDataStore.removeData(ofTypes: WKWebsiteDataStore.allWebsiteDataTypes(), modifiedSince: .distantPast) {
               group.leave()
           }

            group.notify(queue: .main) {
                self.fillWebViewCookieStore(webView.configuration.websiteDataStore.httpCookieStore) {
                    webView.load(request)
                }
            }