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

its been long time , apple did not introduce cookie fix for ios less than 11. if there is no solution for cookie sync for less than ios 11 then why Apple mentinoned in documentation Wkwebview available from iOS 8 😟 :@

@AlexOak a thousand thank yous for posting this solution. It's crazy that this is necessary, but it works. Did you file a bug report with Apple for this? I've spent untold hours trying to figure out why cookies are sometimes set and sometimes not, with different results pretty much every time I ran the app. Just adding the observer fixes the problem completely.

The problem with syncing cookies to WKWebView lies in WKProcessPool. To properly sync cookie, you have to create an instance of WKProcessPool and set it to the WKWebViewConfiguration that is to be used to initialize the WkWebview itself:


    private lazy var mainWebView: WKWebView = {
        let webConfiguration = WKWebViewConfiguration()
        if Enviroment.shared.processPool == nil {
            Enviroment.shared.processPool = WKProcessPool()
        }
        webConfiguration.processPool = Enviroment.shared.processPool!
        webConfiguration.processPool = WKProcessPool()
        let webView = WKWebView(frame: .zero, configuration: webConfiguration)
        webView.navigationDelegate = self
        return webView
    }()


Setting WKProcessPool is the most important step here. WKWebview makes use of process isolation - which means it runs on a different process than the process of your app. This can sometimes cause conflict and prevent your cookie from being synced properly with the WKWebview. If you don't use the same instance of WKProcessPool each time you configure a WKWebView for the same domain (maybe you have a VC A that contains a WKWebView and you want to create different instances of VC A in different places), there can be conflict setting cookies. To solve the problem, after the first creation of the WKProcessPool for a WKWebView that loads domain B, I save it in a singleton and use that same WKProcessPool every time I have to create a WKWebView that loads the same domain B


After the initialization process, you can load an URLRequest inside the completion block of httpCookieStore.setCookie. Here, you have to attach the cookie to the request header otherwise it won't work.


    mainWebView.configuration.websiteDataStore.httpCookieStore.setCookie(your_cookie) {
            self.mainWebView.load(your_request, with: [your_cookie])
    }


    extension WKWebView {
       func load(_ request: URLRequest, with cookies: [HTTPCookie]) {
          var request = request
          let headers = HTTPCookie.requestHeaderFields(with: cookies)
          for (name, value) in headers {
             request.addValue(value, forHTTPHeaderField: name)
          }      
          load(request)
       }
    }

You are not actually using the singleton in the code above. Well you are but then you immediately overwrite it with a fresh WKProcessPool() on line 7.

I have a similar problem. I used to sync HTTPCookieStorage with WKHTTPCookieStore but my cookies not saved and every time it return logout user.