WKHTTPCookieStorage's setCookie doesn't return after closing and opening web view

1.5k views Asked by At

I have the code below to copy cookies from the general HTTPCookieStorage, into my webview's cookie store. After all cookies are copied, I load the webview

    let webView = WKWebView(frame: containerView.bounds, configuration: WKWebViewConfiguration())
    webView.navigationDelegate = navigationDelegate
    containerView.addAndFillSubview(webView)
    guard let sharedCookies = HTTPCookieStorage.shared.cookies else {
        return
    }

    // For each cookie in the common store, set it in the WKWebView's store.
    for sharedCookie in sharedCookies {
        // Add a block to the dispatch group every time a cookie begins to be set in the WKWebView's store.
        wkSetCookieDispatchGroup.enter() 
        webView.configuration.websiteDataStore.httpCookieStore.setCookie(sharedCookie) {
           // Release a block from the dispatch group every time a cookie is successfully set in the WKWebView's store.
           wkSetCookieDispatchGroup.leave()
        }
    }

    // Wait for all the cookies to be successfully set (all blocks in the wkSetCookieDispatchGroup to be released)
    wkSetCookieDispatchGroup.notify(queue: .main) {
       // Load url in webView
    }

The first time this code runs it works - all cookies are set, setCookie's completion handlers are called and the dispatch group is notified.

However, after n runs (usually 3 or 4) of closing this web view and opening it, setCookie's completion handlers STOP returning, usually indefinitely. There is some race condition going on, because if I set break points and hit them every time the web view opens, this issue never occurs. After it occurs once, setting break points sometimes gets it to start working again, and sometimes doesn't do anything.

NOTE: This only happens on device. On simulator, this never occurs.

There's no documentation on when setCookie's completionHandler should return or not return, and the fact that this is a race condition that usually doesn't happen when you have break points makes it incredibly hard to debug.

Has anyone else experienced this? Any advice? I've tried explicitly setting the WKProcessPool in several places, which didn't work. I tried creating the web view before and after creating the configuration. I've tried a bunch of stuff, but still lost

2

There are 2 answers

0
bryan On

Not sure if this will fit with your requirements but I was running into the same issue and found that the same cookie store was being used for each WKWebViewConfiguration that I was creating to be used with a different WKWebView.

What I ended up doing since I would need the same cookies for any wk webview instance I would use is created a singleton WKWebViewConfiguration that adds the needed cookies once, sets a WKProcessPool and then returns a copy.

The for each different wk webview I needed would set its custom configuration on the copy but not have to interact with the cookie store.

Seems to be working for me after running into the same issue.

0
Ashok On

Try to turn off Apple Transport Security in info.plist by adding key Allow Arbitrary Loads to YES. (Just to troubleshoot, don't encourage this as solution even if it solves problem)

In my experience, setCookie completion handler block is NOT called if your app confirms to Apple Transport Security (ATS) and you are trying to call set a non secure cookie.

PS: This seems to be an undocumented fact of WebKit as of iOS 12.2

Below are the details I personally noticed in our iOS app -

We had below code in our ATS compliant app to copy cookies before loading the authenticated request url -

        let cookieGroup = DispatchGroup()
        for cookie in cookieList {
            cookieGroup.enter()
            dataStore.httpCookieStore.setCookie(cookie) { cookieGroup.leave() }
        }
        cookieGroup.notify(queue: DispatchQueue.main) {
            config.websiteDataStore = dataStore
            completion(config)
        }

And below cookie (notice isSecure: FALSE) couldn't set so couldn't execute balancing leave() and thus never executed notify block.

<NSHTTPCookie
    version:1
    name:TESTNAME
    value:BD61035906F6FFFF79D6CEF1F8FFD74ACD03CFC76A758FBBB78C8BC3D6C2C4C3F7B239BBE8C7C3A6FBEF1D903B5AE4FFFF50B9749C2C3593DC2D36FA0CBD695296EC958A
    expiresDate:'2019-05-08 16:10:43 +0000'
    created:'2019-05-07 16:10:43 +0000'
    sessionOnly:FALSE
    domain:SOME_DOMAIN_TEST
    partition:none
    sameSite:none
    path:/
    isSecure:FALSE
 path:"/" isSecure:FALSE>

(changed name and domain value above to obfuscate the source)