Getting all cookies from WKWebView

79,623

Solution 1

Finally, httpCookieStore for WKWebsiteDataStore landed in iOS 11.

https://developer.apple.com/documentation/webkit/wkwebsitedatastore?changes=latest_minor

Solution 2

Cookies used (created) by the WKWebView are actually correctly stored in the NSHTTPCookieStorage.sharedHTTPCookieStorage().

The problem is that the WKWebView does not write back the cookies immediately. I think it does this on its own schedule. For example when a WKWebView is closed or maybe periodically.

So eventually they do end up in there, but when is unpredictable.

You may be able to force a 'sync' to the shared NSHTTPCookieStorage by closing your WKWebView. Please let us know if this works.

Update: I just remembered that in Firefox for iOS we force the WKWebView to flush its internal data, including cookies, by replacing its WKProcessPool with a new one. There is no official API, but I am pretty sure that is the most reliable workaround right now.

Solution 3

Details

  • Xcode 9.2, Swift 4
  • Xcode 10.2 (10E125), Swift 5

Solution

extension WKWebView {

    private var httpCookieStore: WKHTTPCookieStore  { return WKWebsiteDataStore.default().httpCookieStore }

    func getCookies(for domain: String? = nil, completion: @escaping ([String : Any])->())  {
        var cookieDict = [String : AnyObject]()
        httpCookieStore.getAllCookies { cookies in
            for cookie in cookies {
                if let domain = domain {
                    if cookie.domain.contains(domain) {
                        cookieDict[cookie.name] = cookie.properties as AnyObject?
                    }
                } else {
                    cookieDict[cookie.name] = cookie.properties as AnyObject?
                }
            }
            completion(cookieDict)
        }
    }
}

Usage

// get cookies for domain
webView.getCookies(for: url.host) { data in
      print("=========================================")
      print("\(url.absoluteString)")
      print(data)
}

// get all cookies
webView.getCookies() { data in
      print("=========================================")
      print("\(url.absoluteString)")
      print(data)
}

Full sample

Info.plist

add in your Info.plist transport security setting

 <key>NSAppTransportSecurity</key>
 <dict>
    <key>NSAllowsArbitraryLoads</key>
    <true/>
 </dict>

Code

  1. Do not forget to add the solution code here
  2. ViewController has embed view controller
import UIKit
import WebKit

class ViewController: UIViewController {

    private lazy var url = URL(string: "https://google.com")!
    private weak var webView: WKWebView?

    func initWebView(configuration: WKWebViewConfiguration) {
        if webView != nil { return }
        let webView = WKWebView(frame: UIScreen.main.bounds, configuration: configuration)
        webView.navigationDelegate = self
        webView.uiDelegate = self
        view.addSubview(webView)
        self.webView = webView
    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        if webView == nil { initWebView(configuration: WKWebViewConfiguration()) }
        webView?.load(url: url)
    }
}

extension ViewController: WKNavigationDelegate {

    func webView(_ webView: WKWebView, decidePolicyFor navigationResponse: WKNavigationResponse, decisionHandler: @escaping (WKNavigationResponsePolicy) -> Void) {
        decisionHandler(.allow)
    }

    func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
        if let url = webView.url {
            webView.getCookies(for: url.host) { data in
                print("=========================================")
                print("\(url.absoluteString)")
                print(data)
            }
        }
    }
}

extension ViewController: WKUIDelegate {

    func webView(_ webView: WKWebView, createWebViewWith configuration: WKWebViewConfiguration, for navigationAction: WKNavigationAction, windowFeatures: WKWindowFeatures) -> WKWebView? {
        // push new screen to the navigation controller when need to open url in another "tab"
        if let url = navigationAction.request.url, navigationAction.targetFrame == nil {
            let viewController = ViewController()
            viewController.initWebView(configuration: configuration)
            viewController.url = url
            DispatchQueue.main.async { [weak self] in
                self?.navigationController?.pushViewController(viewController, animated: true)
            }
            return viewController.webView
        }
        return nil
    }
}

extension WKWebView {

    func load(urlString: String) {
        if let url = URL(string: urlString) { load(url: url) }
    }

    func load(url: URL) { load(URLRequest(url: url)) }
}

enter image description here

Solution 4

I know this is a very old question, and we have a solution but work only on iOS 11 and upper. For those one who are dealing with iOS 10 and lower (like me), you may consider this method. It works perfectly to me:

  • Force reset processPool:
extension WKWebView {
    func refreshCookies() {
        self.configuration.processPool = WKProcessPool()
        // TO DO: Save your cookies,...
    }
}

--> this only work on real device.

  • For simulator, you should add:
func webView(_ webView: WKWebView, decidePolicyFor navigationResponse: WKNavigationResponse, decisionHandler: @escaping (WKNavigationResponsePolicy) -> Void) {
    if let response = navigationResponse.response as? HTTPURLResponse,
       let allHttpHeaders = response.allHeaderFields as? [String: String],
       let responseUrl = response.url {
        let cookies = HTTPCookie.cookies(withResponseHeaderFields: allHttpHeaders, for: responseUrl)

        for cookie in cookies {
            HTTPCookieStorage.shared.setCookie(cookie)
        }
    }

    decisionHandler(.allow)
}

Follow to the answer of Stefan Arentz and Phenom.

Solution 5

For iOS 11, without any extensions:

func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
    self.webView.configuration.websiteDataStore.httpCookieStore.getAllCookies { cookies in
        for cookie in cookies {
            //...
        }
    }
}
Share:
79,623
aporat
Author by

aporat

It's me

Updated on July 05, 2022

Comments

  • aporat
    aporat almost 2 years

    while getting cookies from UIWebView seems straightforward by using NSHTTPCookieStorage.sharedHTTPCookieStorage(), it seems WKWebView stores the cookies somewhere else.

    I did some research, and I was able to get some cookies from the grabbing it from NSHTTPURLResponse object. this, however, does not contain all the cookies used by WKWebView:

    func webView(webView: WKWebView, decidePolicyForNavigationResponse navigationResponse: WKNavigationResponse, decisionHandler: (WKNavigationResponsePolicy) -> Void) {
    
      if let httpResponse = navigationResponse.response as? NSHTTPURLResponse {
        if let headers = httpResponse.allHeaderFields as? [String: String], url = httpResponse.URL {
          let cookies = NSHTTPCookie.cookiesWithResponseHeaderFields(headers, forURL: url)
    
          for cookie in cookies {
            logDebug(cookie.description)
    
            logDebug("found cookie " + cookie.name + " " + cookie.value)
          }
        }
      }
    }
    

    Strangely, there's also a class WKWebsiteDataStore in ios 9 that responsible for managing cookies in WKWebView, however, the class does not contain a public method to retrieve the cookies data:

    let storage = WKWebsiteDataStore.defaultDataStore()
    
    storage.fetchDataRecordsOfTypes([WKWebsiteDataTypeCookies], completionHandler: { (records) -> Void in
      for record in records {
        logDebug("cookie record is " + record.debugDescription)
    
        for dataType in record.dataTypes {
          logDebug("data type is " + dataType.debugDescription)
    
          // get cookie data??
        }
      }
    })
    

    Is there a workaround for getting the cookie data?

    • robotspacer
      robotspacer almost 8 years
      Worth noting that the WebKit team seems to be working on a proper way to access WKWebView's cookie storage: bugs.webkit.org/show_bug.cgi?id=140191
    • ZAFAR007
      ZAFAR007 about 7 years
      @aporat have you found any solution yet, i am working on this from months but didn't get any solution yet :(
    • Harish Pathak
      Harish Pathak about 6 years
    • Muhammad Shauket
      Muhammad Shauket over 5 years
      @aporat you have not mention get cookie data :)
  • aporat
    aporat over 8 years
    thanks. I'll check it out. Do you have a reference to a function that perform that WKProcessPool switch? Do I just replace the pool with a new one?
  • tapmonkey
    tapmonkey almost 8 years
    Anyone experience with the workaround described in this answer?
  • Lukas Würzburger
    Lukas Würzburger over 7 years
    What do you mean by 'Close the WKWebView'? removeFromSuperview and set it to nil?
  • ikzjfr0
    ikzjfr0 over 7 years
    yes, it's right to say that cookie are stored in NSHTTPCookieStorage.sharedHTTPCookieStorage(). However, the issue is: if an user has signed in UIWebView, it does not automatically signed in another WKWebView, vice visa. So my belief is: even though they share the same cookie, the principle behind them is quite differenent
  • Siva
    Siva over 6 years
    i need help for WKWebView cookies.
  • Phenom
    Phenom over 6 years
    For those looking at this and confused, I literally did: self.webView.configuration.processPool = [[WKProcessPool alloc] init]; and it worked to flush the cookies so they were available inNSHTTPCookieStorage.sharedHTTPCookieStorage(), however it only works on device for me, not simulator.
  • Jonny
    Jonny over 5 years
    Is this answer up to date?
  • Jonny
    Jonny over 5 years
    Did you forget how actually copy the cookies?
  • Shahid Ghafoor
    Shahid Ghafoor almost 5 years
    can I get the complete project?
  • Vasily  Bodnarchuk
    Vasily Bodnarchuk almost 5 years
  • CyberMew
    CyberMew over 4 years
    When is the refreshCookies() called? I guess before we need it to retrieve the cookies? It is still not working for the website I am using, tested on actual device running iOS 13.1.2. Is it because of session/persistent cookies?
  • Jonny
    Jonny about 4 years
    I'm back and voting this up, to get the cookies I added self.configuration.websiteDataStore.httpCookieStore.getAllCo‌​okies { (cookies) in /* your own code */}. Only tested on device so far (iOS 13).
  • Erdogan
    Erdogan about 4 years
    Hi jorge, whats your solution for iOS 13.0 and above?
  • Yogendra Patel
    Yogendra Patel almost 4 years
    What about for iOS 10 or below
  • Yogendra Patel
    Yogendra Patel almost 4 years
    always i get empty "cookies" array. Please help me
  • Yogendra Patel
    Yogendra Patel almost 4 years
    Thanks your answer is help to me, but it's work iOS 11.0 or Above. I want to do same execution for iOS 10 or below. Please help me. Means i need to get all cookies in iOS 10.
  • Junia Montana
    Junia Montana about 2 years
    I am an Android developer, completely new to IOS, just need to solve this issue! So my question is when do you call getAllCookies? After the webview.loadUrl? Or before? Please give me some pointers thank you!
  • Zeero0
    Zeero0 about 2 years
    It needs to be called when a webpage is fully loaded in Webview.
  • Junia Montana
    Junia Montana about 2 years
    much appreciated! I actually solved the problem, it was bit hard for me at the beginning.