Setting cookies with WKHTTPCookieStorage do not sync with wkwebview

I created a method to set cookies from HTTPCookieStorage into WKHTTPCookieStorage, as it seems this api is full async so i created an async method that sets the cookies in wkHTTPCookieStorage.


func copyCookiesToWKStore(completion: @escaping () -> Void) {
        if let httpCookies: [HTTPCookie] = self.httpCookieStorage.cookies {
            print("updating wkCookies with httpCookies: \(httpCookies)")
            func copyCookiesAsync(index: Int, cookieHandler: SDCookiesHandler?) {
                if let cookieHandler = cookieHandler, index < httpCookies.count {
                    let cookie = httpCookies[index]
                    cookieHandler.wkCookieStore.setCookie(cookie, completionHandler: {
                        print("set cookie in Cookie Storage: \(cookie)")
                        DispatchQueue.main.async {
                            copyCookiesAsync(index: index + 1, cookieHandler: cookieHandler)
                        }
                    })
                }
                else {
                    completion()
                }
            }
            weak var cookieHandler = self
            copyCookiesAsync(index: 0, cookieHandler: cookieHandler)
        } else {
            completion()
        }
    }



and the load the request in the wkwebview:


wkCookiesHandler.copyCookiesToWKStore { [weak self] in
    if let weakSelf = self {
       DispatchQueue.main.async {
          weakSelf.webView.load(cookiesRequest)
       }
    }



However when webview calls the decidePolicyFor navigationAction method, the cookies in WKCookieStorage are not fully synced at this point.

I have read in some posts that WKProcessPool has its own cycle to sync cookies, and that resetting it, causes cookies to sync with wkwebview, but i found that i am in existent webview flow, it causes weird artifacts with the webview.

Has anyone started using WKCookieStorage api, and how can one keep cookies inserted in this api, visible by the WKWebview.

Replies

Hi,


I have a similar problem. I used to sync HTTPCookieStorage with WKHTTPCookieStore with the following method.

@available(iOS 11.0, *)
    func fillCookieStore(store: WKHTTPCookieStore, completionHandler: (() -> Void)? = nil) {
       
        DDLogDebug("HttpCookieStorage: \(String(describing: HTTPCookieStorage.shared.cookies))")
       
        if let cookies = HTTPCookieStorage.shared.cookies {
           
            let sem = DispatchGroup()
            for cookie in cookies {
                sem.enter()
                store.setCookie(cookie, completionHandler: {
                    sem.leave()
                })
            }
           
            sem.notify(queue: .main) {
                completionHandler?()
            }
        } else {
            completionHandler?()
        }
    }


This used to work just fine until iOS 11.3 Beta. Since then my cookies are not available anymore. I have no clue what to do.


Best regards

Peter

Hi Peter,


Does your code work well with iOS versions before 11.3, (11.3 is still in beta so you might have a solution). Do you insert your cookies before setting the configuration into webview, or does this work even if the configuration is set?

Most of our cases scenarios require us to set the cookies to an existent webview and a share WKProcessPool.



Regards

Hi,

it work's well for all version greater 11.0 and before 11.3. I insert the cookies AFTER setting the configuration to webview. Out of curiosity I tried to insert them before. But that doesnt work.


Best regards

Peter

This is rather strange, just out of curiosity how many webviews instances do you have running and also are your WKProcessPools shared or isolated.

I found that when i set the cookies before i call load(request) in the wkwebview, the cookies werent updated when decided decidePolicyFor navigationAction: WKNavigationAction is called.


At this point the webview is part of the its superview, the configuration is set, and it is using a shared WKProcessPool, i found if i create at this point a new process pool the the cookies are inserted, but sometimes pages dont load correctly...


When do you set the cookies, before or after adding it to subview?


Apologies for picking your brain, just trying to understand the differences between ur approach and mine, when i finished adding my cookies to WKCookiestorage, if get all cookies i could see them there, but not when i called them at the decidePolicyFor navigationAction: WKNavigationAction method, which is rather strange.


To me it seems its all related to the WKProcessPool and it update schedule, but maybe i am wrong.

I'm having an identical issue. I have seent the same cookie behavior as described above. Cookies report as being in the WHHTTPCookieStore and I wait for the callbacks to finish, but the WKWebView (process pool?) doesn't seem to have them 100% of the time when the request is made.

I see random success, indicated a race condition, or in other words, likely a process pool cookie sync scheduling issue

We have apps in production with this problem since iOS 11.3 was released causing users to give us 1 stars ratings. Apple must find a solution to this problem ASAP. Our code is really not different than the others solutions I see here.

Same problem for us. Our approach is very similar to the two posted here. Everything worked fine on every device prior to 11.3, but it fails on every device running 11.3 and above.

Below are some code snippets from our implementation. Again, all of this worked fine prior to iOS 11.3. I tried adding an observer to the WKHTTPCookieStore associated with the WKWebView instance, but "cookiesDidChange" never seems to be hit.


   
     // Create the WKWebView instance. Add a cookie store observer.
    if #available(iOS 11, *){
        wkWebView = WKWebView()
        wkWebView!.scrollView.delegate = self
        wkWebView!.configuration.websiteDataStore.httpCookieStore.add(OnShiftWKHTTPCookieStoreObserver())
    }


    // this is how we're adding cookies to our WKWebView instance.
    public func writeCookies(cookies: [[HTTPCookiePropertyKey : Any]]) {
        if #available(iOS 11, *) {
            let cookiesStore = wkWebView.configuration.websiteDataStore.httpCookieStore
            cookies.forEach { (cookie) in
                guard let cookie = HTTPCookie.init(properties: cookie) else {
                    return
                }
                cookiesStore.setCookie(cookie, completionHandler: {
                    print ("cookie added: \(cookie.name)")
                })
            }
        }
    }


// This is our WKHTTPCookieStoreObserver implementation. Note that "cookiesDidChange" isn't called
// even after several calls to setCookie above.
class OnShiftWKHTTPCookieStoreObserver: NSObject, WKHTTPCookieStoreObserver {
    @available(iOS 11.0, *)
    func cookiesDidChange(in cookieStore: WKHTTPCookieStore) {
        cookieStore.getAllCookies({(cookies: [HTTPCookie]) in
            cookies.forEach({(cookie: HTTPCookie) in
                print("COOKIE name: \(cookie.name) domain: \(cookie.domain) value: \(cookie.value)")
            })
        })
    }
}

I spent some time on this issue as well. I managed to get my app working again, with the following two alterations:


1. Before creating the WKWebView, observe the default WKWebsiteDataStore instance for cookie changes.


// These two lines occur in the viewDidLoad method of a UIViewController class
// This view controller conforms to the WKHTTPCookieStoreObserver protocol
WKWebsiteDataStore.default().httpCookieStore.add(self)
let webView = WKWebView()
// Configure and load the web view


2. In the cookiesDidChange(in:) method of your WKHTTPCookieStoreObserver, retrieve the cookies from the WKHTTPCookieStore asynchronously, on the main thread.


func cookiesDidChange(in cookieStore: WKHTTPCookieStore) {
    DispatchQueue.main.async {
          cookieStore.getAllCookies { cookies in
              // Process cookies
          }
    }
}


Now my app runs successfully on iOS 11.3 and 11.3.1.

Thank you! This appears to fix our problem as well.

I finally made this work again. My error was kind of trivial 🙂 I found a working solution in last years wwdc video around minute 18. https://developer.apple.com/videos/play/wwdc2017/220/


When I compared this to my code, I saw that I was using my WKWebViewConfiguration's WKWebsiteDataStore without setting it before usage. This used to work. But it looks like it doesnt anymore. So all I endet up doing was this:

let store = WKWebsiteDataStore.nonPersistent()
webConfiguration.websiteDataStore = store

I found this solution as well by looking at the WWDC session. However, I shortly thereafter discovered that using a non-persistent WKWebsiteDataStore had other problems. The website I'm integrating was using Web Storage. It appears that if using a non-persistent store, data stored in Window.sessionStorage or Window.localStorage will not be retained over a page refresh. Thus I'm back to square one.


If anyone has any idea on how to reliably set cookies even on the default store, I'd be happy to hear.

Hi! I'd like to just ask for a clarification here – what problem does this solution solve?


Does observing the cookie store and getting all the cookies on the main thread actually help with the problem of _setting_ cookies?

*bump
Having a similar issue where not all cookies are passed to ajax (and following requests). Any idea on how to set cookies for all calls inside the WKWebview ?

no one mentioned how to sync cookies less than ios version 11 using wkwebview.