Alle Cookies von WKWebView erhalten

70

Während das Abrufen von Cookies UIWebViewdurch die Verwendung unkompliziert NSHTTPCookieStorage.sharedHTTPCookieStorage()erscheint, WKWebViewwerden die Cookies anscheinend an einem anderen Ort gespeichert.

Ich habe einige Nachforschungen angestellt und konnte einige Kekse erhalten, indem ich sie vom NSHTTPURLResponseObjekt genommen habe. Dies enthält jedoch nicht alle Cookies, die verwendet werden von 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)
      }
    }
  }
}

Seltsamerweise gibt es WKWebsiteDataStorein ios 9 auch eine Klasse , die für die Verwaltung von Cookies verantwortlich WKWebViewist. 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 zum Abrufen der Cookie-Daten?

Aporat
quelle
Erwähnenswert ist, dass das WebKit-Team anscheinend an einem geeigneten Weg arbeitet, um auf den Cookie-Speicher von WKWebView
zuzugreifen
@aporat hast du noch eine Lösung gefunden, ich arbeite seit Monaten daran, habe aber noch keine Lösung bekommen :(
ZAFAR007
@aporat Sie haben nicht erwähnt, erhalten Sie Cookie-Daten :)
Shauket Sheikh

Antworten:

74

Von der verwendeten (erstellten) Cookies WKWebViewwerden tatsächlich korrekt in der gespeichert NSHTTPCookieStorage.sharedHTTPCookieStorage().

Das Problem ist, dass WKWebViewdie Cookies nicht sofort zurückgeschrieben werden. Ich denke, das macht es nach eigenem Zeitplan. Zum Beispiel, wenn a WKWebViewgeschlossen ist oder in regelmäßigen Abständen.

Also landen sie schließlich dort, aber wann ist unvorhersehbar.

Möglicherweise können Sie eine Synchronisierung mit der Freigabe erzwingen, NSHTTPCookieStorageindem Sie Ihre schließen WKWebView. Bitte lassen Sie uns wissen, ob dies funktioniert.

Update : Ich habe mich gerade daran erinnert, dass wir in Firefox für iOS die WKWebViewinternen Daten, einschließlich Cookies, erzwingen müssen , indem wir sie durch WKProcessPoolneue ersetzen . Es gibt keine offizielle API, aber ich bin mir ziemlich sicher, dass dies derzeit die zuverlässigste Problemumgehung ist.

Stefan Arentz
quelle
8
Vielen Dank. Ich werde das überprüfen. Haben Sie einen Verweis auf eine Funktion, die diesen WKProcessPool-Schalter ausführt? Ersetze ich den Pool einfach durch einen neuen?
Aporat
Hat jemand Erfahrung mit der in dieser Antwort beschriebenen Problemumgehung?
Tapmonkey
Was meinst du mit 'Close the WKWebView'? removeFromSuperview und setze es auf nil?
Lukas Würzburger
Ich brauche Hilfe für WKWebView-Cookies.
Siva
7
Für diejenigen, die dies betrachten und verwirrt sind, habe ich es buchstäblich getan: self.webView.configuration.processPool = [[WKProcessPool alloc] init]; und es hat funktioniert, um die Cookies zu spülen, damit sie verfügbar sind NSHTTPCookieStorage.sharedHTTPCookieStorage(), aber es funktioniert nur auf dem Gerät für mich, nicht auf dem Simulator.
Phänomen
20

Einzelheiten

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

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

Verwendung

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

Vollständige Probe

Info.plist

Fügen Sie Ihre Transportsicherheitseinstellung Info.plist hinzu

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

Code

  1. Vergessen Sie nicht, den Lösungscode hier hinzuzufügen
  2. ViewController hat View Controller eingebettet
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)) }
}

Geben Sie hier die Bildbeschreibung ein

Wassili Bodnarchuk
quelle
Kann ich das komplette Projekt bekommen?
Shahid Ghafoor
@ ShahidGhafoor sicher, dropbox.com/s/kft7ue4zgn4p5hl/stackoverflow-33156567.zip?dl=0
Vasily Bodnarchuk
14

Ich weiß, dass dies eine sehr alte Frage ist, und wir haben eine Lösung, arbeiten aber nur unter iOS 11 und höher. Für diejenigen, die mit iOS 10 und niedriger zu tun haben (wie ich), können Sie diese Methode in Betracht ziehen. Es funktioniert perfekt für mich:

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

-> Dies funktioniert nur auf realen Geräten.

  • Für den Simulator sollten Sie 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.

Novemberregen
quelle
Haben Sie vergessen, wie die Cookies tatsächlich kopiert werden?
Jonny
Wann wird der refreshCookies()angerufen? Ich denke, bevor wir es brauchen, um die Cookies abzurufen? Es funktioniert immer noch nicht für die Website, die ich verwende und die auf einem tatsächlichen Gerät mit iOS 13.1.2 getestet wurde. Liegt es an Sitzungs- / dauerhaften Cookies?
CyberMew
Ich bin zurück und stimme ab, um die Cookies zu erhalten, die ich hinzugefügt habe self.configuration.websiteDataStore.httpCookieStore.getAllCookies { (cookies) in /* your own code */}. Bisher nur auf dem Gerät getestet (iOS 13).
Jonny
Ich bekomme immer ein leeres "Cookies" -Array. Bitte helfen Sie mir
Yogendra Patel
4

Ich habe WKHTTPCookieStore in Objective-C verwendet. Dies hat bei mir funktioniert, um sowohl dauerhafte als auch 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 Löschens der internen Daten durch WKWebView durch Ersetzen des WKProcessPool, wie in Stefans Antwort beschrieben, funktionierte für mich in iOS 10 und 11, jedoch nur für dauerhafte Cookies. Es scheint, als würden Sitzungscookies entfernt, wie J. Thoo beschrieben hat

Jorge Duque
quelle
Hallo Jorge, was ist deine Lösung für iOS 13.0 und höher?
Erdogan
4

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 {
            //...
        }
    }
}
Zeero0
quelle
3
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];
      }];
}
Vivek
quelle
Vielen Dank, Ihre Antwort ist Hilfe für mich, aber es funktioniert iOS 11.0 oder höher. Ich möchte die gleiche Ausführung für iOS 10 oder niedriger durchführen. Bitte hilf mir. Bedeutet, ich muss alle Cookies in iOS 10 bekommen.
Yogendra Patel
3

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)
}
Den
quelle
2

Wie bereits von Stefan erwähnt, werden Cookies in gespeichert NSHTTPCookieStorage.sharedHTTPCookieStorage()

Bei meinen Experimenten stellte ich jedoch fest, dass vom Server gesetzte Sitzungscookies für nicht sichtbar sind NSHTTPCookieStorage.sharedHTTPCookieStorage().

Solange jeder WKWebViewdieselbe Instanz von gemeinsam WKProcessPoolnutzt, werden diese Sitzungscookies für jede Anforderung an den Server zurückgegeben. Wenn Sie den Prozesspool für a ändern WKWebView, entfernen Sie im Wesentlichen die Sitzungscookies für alle zukünftigen Anforderungen.

J.Thoo
quelle
Ja, es ist richtig zu sagen, dass Cookies in NSHTTPCookieStorage.sharedHTTPCookieStorage () gespeichert sind. Das Problem ist jedoch: Wenn sich ein Benutzer in UIWebView angemeldet hat, wird er nicht automatisch in einem anderen WKWebView-Vize-Visum angemeldet. Mein Glaube ist also: Obwohl sie denselben Cookie verwenden, ist das Prinzip hinter ihnen ganz anders
ikzjfr0
1

Verschwenden Sie keine Zeit mit dem Extrahieren von Cookies iOS 11 below device, da die Erfolgsaussichten sehr gering sind. Das Extrahieren von Cookies kann aus Sicherheitsgründen blockiert werden.

Verweisen Sie auf 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 Geräte unter iOS 11 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)
    }

Mit dem obigen Code erhalten Sie ein leeres Cookie-Array, da die Extraktion von Cookies aus Sicherheitsgründen blockiert wird.

Ich würde Ihnen empfehlen, Folgendes zu versuchen, das für iOS 11 und höher gedacht ist:

WKWebsiteDataStore.default().httpCookieStore.getAllCookies { (cookies) in
    for cookie in cookies {
        print(cookie)
    }
}
Shubham Mishra
quelle
0

In der Praxis habe ich in der Methode "entscheidendPolicyForNavigationResponse" festgestellt, dass Sie Cookies auf folgende Weise abrufen können. Das Traurige ist jedoch, dass es sich nicht um eine vollständige Liste für eine Sitzung handelt.

let response = navigationResponse.response as! NSHTTPURLResponse
        let headFields = response.allHeaderFields as! [String:String]

        let cookies = NSHTTPCookie.cookiesWithResponseHeaderFields(headFields, forURL: response.URL!)
ikzjfr0
quelle
0

In NSHTTPCookie.cookiesWithResponseHeaderFields(headers, forURL: url), was passiert , wenn die URL , wo die Cookies gesetzt werden ist keine Navigation Antwort url (url , die eine Navigation verursacht)? Ich stelle fest, dass die Rückruf-URL, unter der die Cookies gesetzt werden, in entscheidendPolicyFor Navigationsantwort nie 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ückruf-URL ausgeführt, da der Rückruf selbst keine Seitennavigation verursacht.

Cookies (withResponseHeaderFields: für :)

Qin Zhengquan
quelle
-1

Dieser Beitrag enthält nützliche Informationen zum Umgang mit Cookies mit WKWebView. Demnach sollten Sie in der Lage sein, Cookies mit dem Standard-NSURLCache und NSHTTPCookie zu setzen und abzurufen. Er bezieht sich auch auf die Verwendung von WKProccessPool gemäß Stephans Kommentar.

Fuad Kamal
quelle