web-dev-qa-db-fra.com

Obtenir tous les cookies de WKWebView

obtenir des cookies de UIWebView semble simple en utilisant NSHTTPCookieStorage.sharedHTTPCookieStorage(), il semble que WKWebView stocke les cookies ailleurs.

J'ai fait des recherches et j'ai pu obtenir des cookies en les saisissant d'un objet NSHTTPURLResponse. ceci, cependant, ne contient pas tous les cookies utilisés par 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)
      }
    }
  }
}

Étrangement, il existe également une classe WKWebsiteDataStore dans ios 9 responsable de la gestion des cookies dans WKWebView; toutefois, la classe ne contient pas de méthode publique pour récupérer les données des cookies:

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

Existe-t-il une solution de rechange pour obtenir les données de cookie?

46
aporat

Enfin, httpCookieStore pour WKWebsiteDataStore a atterri dans iOS 11.

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

13
Tualatrix Chou

Les cookies utilisés (créés) par la WKWebView sont en fait correctement stockés dans la NSHTTPCookieStorage.sharedHTTPCookieStorage().

Le problème est que la WKWebView ne réécrit pas les cookies immédiatement. Je pense qu'il le fait sur son propre calendrier. Par exemple, lorsque WKWebView est fermé ou peut-être périodiquement.

Ils finissent donc par arriver là, mais when est imprévisible.

Vous pourrez peut-être forcer une 'synchronisation' avec la NSHTTPCookieStorage partagée en fermant votre WKWebView. S'il vous plaît laissez-nous savoir si cela fonctionne.

Update: Je viens de me rappeler que dans Firefox pour iOS nous forçons la WKWebView à vider ses données internes, y compris les cookies, en remplaçant sa WKProcessPool par une nouvelle. Il n'y a pas d'API officielle, mais je suis à peu près sûr que c'est la solution la plus fiable actuellement.

55
Stefan Arentz

Détails

Xcode 9.2, Swift 4

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

Échantillon complet

  1. N'oubliez pas de ajouter le code de solution ici
  2. ViewController a intégré le contrôleur de vue
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
Vasily Bodnarchuk

Je sais que la question est très ancienne et que nous avons une solution mais ne fonctionnons que sur iOS 11 et supérieur. Pour ceux qui traitent avec iOS 10 et inférieur (comme moi), vous pouvez envisager cette méthode. Cela fonctionne parfaitement pour moi:

  • Forcer la réinitialisation de processPool:

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

-> cela ne fonctionne que sur un appareil réel.

  • Pour simulateur, vous devez ajouter:

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

Suivez les réponses de Stefan Arentz et Phenom.

8
November Rain

J'ai utilisé WKHTTPCookieStore dans Objective-C. Cela m'a permis d'obtenir à la fois des cookies persistants et des cookies de session, mais cela ne fonctionne que sous 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);
        }
 }];

Forcer le WKWebView à vider ses données internes en remplaçant son WKProcessPool comme décrit dans la réponse de Stefan a fonctionné pour moi dans iOS 10 et 11, mais uniquement pour les cookies persistants; il semble que les cookies de session soient supprimés, comme l'a décrit J. Thoo

2
Jorge Duque

Comme Stefan l’a mentionné, les cookies sont stockés dans NSHTTPCookieStorage.sharedHTTPCookieStorage()

Cependant, mes expériences m'ont révélé que les cookies de session définis par le serveur ne sont pas visibles par NSHTTPCookieStorage.sharedHTTPCookieStorage().

Tant que chaque WKWebView partage la même instance de WKProcessPool, ces cookies de session seront retransmis au serveur pour chaque demande. Si vous modifiez le pool de processus pour une WKWebView, vous supprimez essentiellement les cookies de session pour toutes les demandes futures.

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

Ne perdez pas votre temps à extraire les cookies de iOS 11 below device, il y a très peu de chances de réussir. L'extraction de cookies peut être bloquée pour des raisons de sécurité.

Reportez-vous à ces journaux:

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

Essayez ce code qui est construit pour les appareils iOS 11 suivants:

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

Le code ci-dessus vous donnera un tableau de cookies vide, car l'extraction des cookies est bloquée pour des raisons de sécurité.

Je vous recommanderais d'essayer ce qui suit est destiné à iOS 11 et supérieur:

WKWebsiteDataStore.default().httpCookieStore.getAllCookies { (cookies) in
    for cookie in cookies {
        print(cookie)
    }
}
0
HSAM

Dans NSHTTPCookie.cookiesWithResponseHeaderFields(headers, forURL: url), que se passe-t-il si l'URL où sont installés les cookies n'est pas une URL de réponse à la navigation (l'URL qui provoque une navigation)? Je remarque que l'URL de rappel où les cookies sont définis n'est jamais appelée dans decidePolicyFor navigationResponse. 

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

Le délégué ci-dessus n'est jamais exécuté pour l'URL de rappel car le rappel lui-même ne provoque pas de navigation dans les pages. 

cookies (withResponseHeaderFields: pour :)

0
Qin Zhengquan

En pratique, dans la méthode "decidePolicyForNavigationResponse", vous pouvez utiliser la méthode suivante pour récupérer les cookies, mais ce qui est triste, c'est que ce n'est pas une liste complète/complète pour une session.

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

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