web-dev-qa-db-de.com

Alle Cookies von WKWebView erhalten

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?

46
aporat

Schließlich landete httpCookieStore für WKWebsiteDataStore in iOS 11.

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

13
Tualatrix Chou

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.

55
Stefan Arentz

Einzelheiten

Xcode 9.2, Swift 4

Lösung

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)
        }
    }
}

Verwendungszweck

// 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)
}

Volle probe

  1. Vergessen Sie nicht, fügen Sie hier den Lösungscode hinzu
  2. 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))
            }
        }
    }
}

 enter image description here

14

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:

  • Rücksetzen von processPool erzwingen:

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

-> das funktioniert nur auf echten Geräten.

  • Für den Simulator sollten Sie Folgendes hinzufügen:

    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.

8
November Rain

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+

https://developer.Apple.com/documentation/webkit/wkhttpcookiestore?changes=latest_minor&language=objc

 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

2
Jorge Duque

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.

1
J.Thoo
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];
      }];
}
0
Vivek

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)
    }
}
0
HSAM

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)
}
0
Den

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 {
            //...
        }
    }
}
0
Zeero0

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. 

cookies (withResponseHeaderFields: for :)

0
Qin Zhengquan

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!)
0
ikzjfr0