Getting all cookies from WKWebView
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
- Do not forget to add the solution code here
- 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)) }
}
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 {
//...
}
}
}
Comments
-
aporat almost 2 years
while getting cookies from
UIWebView
seems straightforward by usingNSHTTPCookieStorage.sharedHTTPCookieStorage()
, it seemsWKWebView
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 byWKWebView
: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 inWKWebView
, 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 almost 8 yearsWorth 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 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 about 6 years
-
Muhammad Shauket over 5 years@aporat you have not mention get cookie data :)
-
-
aporat over 8 yearsthanks. 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 almost 8 yearsAnyone experience with the workaround described in this answer?
-
Lukas Würzburger over 7 yearsWhat do you mean by 'Close the
WKWebView
'? removeFromSuperview and set it to nil? -
ikzjfr0 over 7 yearsyes, 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 over 6 yearsi need help for WKWebView cookies.
-
Phenom over 6 yearsFor 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 over 5 yearsIs this answer up to date?
-
Jonny over 5 yearsDid you forget how actually copy the cookies?
-
Shahid Ghafoor almost 5 yearscan I get the complete project?
-
Vasily Bodnarchuk almost 5 years@ShahidGhafoor sure, dropbox.com/s/kft7ue4zgn4p5hl/stackoverflow-33156567.zip?dl=0
-
CyberMew over 4 yearsWhen 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 about 4 yearsI'm back and voting this up, to get the cookies I added
self.configuration.websiteDataStore.httpCookieStore.getAllCookies { (cookies) in /* your own code */}
. Only tested on device so far (iOS 13). -
Erdogan about 4 yearsHi jorge, whats your solution for iOS 13.0 and above?
-
Yogendra Patel almost 4 yearsWhat about for iOS 10 or below
-
Yogendra Patel almost 4 yearsalways i get empty "cookies" array. Please help me
-
Yogendra Patel almost 4 yearsThanks 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 about 2 yearsI 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 about 2 yearsIt needs to be called when a webpage is fully loaded in Webview.
-
Junia Montana about 2 yearsmuch appreciated! I actually solved the problem, it was bit hard for me at the beginning.