während das Abrufen von Cookies von UIWebView
durch die Verwendung von NSHTTPCookieStorage.sharedHTTPCookieStorage()
einfach erscheint, scheint es, dass WKWebView
die Cookies an einem anderen Ort speichert.
Ich habe einige Nachforschungen angestellt, und ich konnte ein paar Cookies aus dem Objekt NSHTTPURLResponse
holen. Dieses enthält jedoch nicht alle von WKWebView
verwendeten Cookies:
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)
}
}
}
}
Seltsamerweise gibt es in ios 9 auch eine Klasse WKWebsiteDataStore
, die für die Verwaltung von Cookies in WKWebView
zuständig ist. Die Klasse enthält jedoch keine öffentliche Methode zum Abrufen der Cookie-Daten:
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??
}
}
})
Gibt es eine Problemumgehung für das Abrufen der Cookie-Daten?
Schließlich landete httpCookieStore
für WKWebsiteDataStore
in iOS 11.
https://developer.Apple.com/documentation/webkit/wkwebsitedatastore?changes=latest_minor
Cookies, die von WKWebView
verwendet (erstellt) werden, werden tatsächlich in der NSHTTPCookieStorage.sharedHTTPCookieStorage()
korrekt gespeichert.
Das Problem ist, dass WKWebView
die Cookies nicht sofort zurückschreibt. Ich denke, dass dies nach eigenem Zeitplan geschieht. Zum Beispiel, wenn eine WKWebView
geschlossen ist oder möglicherweise periodisch.
Irgendwann landen sie also dort, aber wann ist unvorhersehbar.
Möglicherweise können Sie eine Synchronisierung mit dem freigegebenen NSHTTPCookieStorage
erzwingen, indem Sie Ihr WKWebView
schließen. Bitte lassen Sie uns wissen, ob dies funktioniert.
Update: Ich erinnere mich gerade daran, dass in Firefox für iOS / wir die WKWebView
zwingen, ihre internen Daten einschließlich Cookies zu leeren, indem sie ihre WKProcessPool
durch eine neue ersetzt. Es gibt keine offizielle API, aber ich bin mir ziemlich sicher, dass dies derzeit die zuverlässigste Lösung ist.
Xcode 9.2, Swift 4
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)
}
}
}
// 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)
}
- Vergessen Sie nicht, fügen Sie hier den Lösungscode hinzu
- ViewController hat View Controller eingebettet
import UIKit
import WebKit
class ViewController: UIViewController {
var urlString = "http://google.com"
var webView: WKWebView!
fileprivate var webViewIsInited = false
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewWillLayoutSubviews() {
if !webViewIsInited {
webViewIsInited = true
if webView == nil {
webView = WKWebView(frame: UIScreen.main.bounds, configuration: WKWebViewConfiguration())
}
view.addSubview(webView)
webView.navigationDelegate = self
webView.uiDelegate = self
webView.loadUrl(string: urlString)
}
}
}
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? {
if navigationAction.targetFrame == nil {
let vc = ViewController()
vc.urlString = navigationAction.request.url?.absoluteString ?? "http://google.com"
vc.view.frame = UIScreen.main.bounds
vc.webView = WKWebView(frame: UIScreen.main.bounds, configuration: configuration)
navigationController?.pushViewController(vc, animated: false)
return vc.webView
}
return nil
}
}
extension WKWebView {
func loadUrl(string: String) {
if let url = URL(string: string) {
if self.url?.Host == url.Host {
self.reload()
} else {
load(URLRequest(url: url))
}
}
}
}
Ich weiß, dass dies eine sehr alte Frage ist, und wir haben eine Lösung, funktionieren aber nur unter iOS 11 und höher. Für diejenigen, die mit iOS 10 und niedriger (wie ich) zu tun haben, können Sie diese Methode in Betracht ziehen. Es funktioniert perfekt für mich:
extension WKWebView {
func refreshCookies() {
self.configuration.processPool = WKProcessPool()
// TO DO: Save your cookies,...
}
}
-> das funktioniert nur auf echten Geräten.
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)
}
Folgen Sie der Antwort von Stefan Arentz und Phenom.
Ich habe WKHTTPCookieStore in Objective-C verwendet. Dies funktionierte für mich, um permanente Cookies und Sitzungscookies zu erhalten, aber es funktioniert nur in iOS 11+
if (@available(iOS 11.0, *)) {
WKHTTPCookieStore *cookieStore = _webView.configuration.websiteDataStore.httpCookieStore;
[cookieStore getAllCookies:^(NSArray* cookies) {
NSHTTPCookie *cookie;
for(cookie in cookies){
NSLog(@"cookie: %@", cookie);
}
}];
Das Erzwingen des WKWebView zum Ersetzen seiner internen Daten durch Ersetzen seines WKProcessPools, wie von Stefans Antwort beschrieben, funktionierte für mich in iOS 10 und 11, jedoch nur für permanente Cookies. Es scheint, als würden Session-Cookies entfernt, wie von J. Thoo beschrieben
Wie Stefan erwähnt, werden Cookies in NSHTTPCookieStorage.sharedHTTPCookieStorage()
gespeichert.
Bei meinen Versuchen habe ich jedoch festgestellt, dass vom Server gesetzte Session-Cookies für NSHTTPCookieStorage.sharedHTTPCookieStorage()
nicht sichtbar sind.
Solange jeder WKWebView
dieselbe Instanz von WKProcessPool
verwendet, werden diese Sitzungscookies für jede Anforderung an den Server zurückgegeben. Wenn Sie den Prozesspool für WKWebView
ändern, entfernen Sie im Wesentlichen die Sitzungscookies für alle zukünftigen Anforderungen.
if (@available(iOS 11.0, *)) {
[webView.configuration.websiteDataStore.httpCookieStore
getAllCookies:^(NSArray<NSHTTPCookie *> *_Nonnull cookies) {
NSURLRequest *request =
[[NSURLRequest alloc] initWithURL:self.URL]; //your URL
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *task = [session
dataTaskWithRequest:request
completionHandler:^(NSData *responseData, NSURLResponse *response,
NSError *error) {
//Do Something
}];
[task resume];
[session.configuration.HTTPCookieStorage storeCookies:cookies forTask:task];
}];
}
Verschwenden Sie keine Zeit mit dem Extrahieren von Cookies aus iOS 11 below device
, da die Chancen, erfolgreich zu sein, sehr geringer sind. Die Cookie-Extraktion kann aus Sicherheitsgründen blockiert werden.
Siehe diese Protokolle:
2019-02-07 00:05:45.548880+0530 MyApp[2278:280725] [BoringSSL] nw_protocol_boringssl_get_output_frames(1301) [C8.1:2][0x10fd776f0] get output frames failed, state 8196
2019-02-07 00:05:45.550915+0530 MyApp[2278:280725] TIC Read Status [8:0x0]: 1:57
Probieren Sie diesen Code aus, der für iOS 11-Geräte erstellt wurde:
func webView(_ webView: WKWebView, decidePolicyFor navigationResponse: WKNavigationResponse, decisionHandler: @escaping (WKNavigationResponsePolicy) -> Void) {
let cookieValue = HTTPCookieStorage.shared.cookies(for: navigationResponse.response.url!)
print(cookieValue!)
let response = navigationResponse.response as! HTTPURLResponse
let headFields = response.allHeaderFields as! [String:String]
let cookies = HTTPCookie.cookies(withResponseHeaderFields: headFields, for: response.url!)
for cookie in cookies {
print("name: \(cookie.name) value: \(cookie.value)")
}
decisionHandler(.allow)
}
Der obige Code gibt Ihnen ein leeres Cookie-Array, da die Extraktion von Cookies aus Sicherheitsgründen blockiert wird.
Ich würde empfehlen, folgendes zu versuchen, was für iOS 11 und höher gedacht ist:
WKWebsiteDataStore.default().httpCookieStore.getAllCookies { (cookies) in
for cookie in cookies {
print(cookie)
}
}
Swift 5
func webView(_ webView: WKWebView, decidePolicyFor navigationResponse: WKNavigationResponse, decisionHandler: @escaping (WKNavigationResponsePolicy) -> Void) {
webView.configuration.websiteDataStore.httpCookieStore.getAllCookies { cookies in
debugPrint(cookies.debugDescription)
}
decisionHandler(.allow)
}
Für iOS 11 ohne Erweiterungen:
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
self.webView.configuration.websiteDataStore.httpCookieStore.getAllCookies { cookies in
for cookie in cookies {
//...
}
}
}
Was passiert in NSHTTPCookie.cookiesWithResponseHeaderFields(headers, forURL: url)
, wenn die URL, in der die Cookies gesetzt sind, keine Navigationsantwort-URL (URL, die eine Navigation verursacht) ist? Ich stelle fest, dass die Callback-URL, in der die Cookies gesetzt sind, niemals in decisionPolicyFor navigationResponse aufgerufen wird.
func webView(_ webView: WKWebView, decidePolicyFor navigationResponse: WKNavigationResponse, decisionHandler: @escaping (WKNavigationResponsePolicy) -> Void) {
let response = navigationResponse.response as! HTTPURLResponse
let cookies = HTTPCookie.cookies(withResponseHeaderFields: response.allHeaderFields as! [String : String], for: response.url!)
}
Der obige Delegat wird niemals für die Rückmeldungs-URL ausgeführt, da der Rückruf selbst keine Seitennavigation verursacht hat.
In der Praxis habe ich in der Methode "decisionPolicyForNavigationResponse" gefunden, dass Sie Cookies abrufen können, aber das Traurige daran ist, dass es keine vollständige/vollständige Liste für eine Sitzung ist.
let response = navigationResponse.response as! NSHTTPURLResponse
let headFields = response.allHeaderFields as! [String:String]
let cookies = NSHTTPCookie.cookiesWithResponseHeaderFields(headFields, forURL: response.URL!)