ios - Obtendo todos os cookies do WKWebView



swift uiwebview (9)

Enquanto os cookies do UIWebView parecem simples, usando o NSHTTPCookieStorage.sharedHTTPCookieStorage() , parece que o WKWebView armazena os cookies em outro lugar.

Eu fiz algumas pesquisas, e consegui pegar alguns cookies do objeto NSHTTPURLResponse . isso, no entanto, não contém todos os cookies usados ​​pelo 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)
      }
    }
  }
}

Estranhamente, há também uma classe WKWebsiteDataStore no ios 9 responsável por gerenciar cookies no WKWebView , no entanto, a classe não contém um método público para recuperar os dados dos 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 uma solução alternativa para obter os dados do cookie?

https://src-bin.com


Answer #1

Detalhes

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

Solução

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

Uso

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

Amostra completa

Info.plist

adicione sua configuração de segurança de transporte Info.plist

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

Código

  1. Não esqueça de adicionar o código da solução aqui
  2. ViewController incorpora controlador de visão
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)) }
}


Answer #2

Como Stefan mencionou, os cookies são armazenados em NSHTTPCookieStorage.sharedHTTPCookieStorage()

No entanto, em meus experimentos, descobri que os cookies da sessão definidos pelo servidor não são visíveis para NSHTTPCookieStorage.sharedHTTPCookieStorage() .

Desde que cada WKWebView compartilhe a mesma instância de WKProcessPool , esses cookies de sessão serão passados ​​de volta para o servidor para cada solicitação. Se você alterar o pool de processos de um WKWebView , estará essencialmente removendo os cookies de sessão para todas as solicitações futuras.


Answer #3

Eu sei que esta é uma questão muito antiga, e nós temos uma solução, mas funcionam apenas no iOS 11 e superior. Para aqueles que estão lidando com o iOS 10 e menor (como eu), você pode considerar este método. Isso funciona perfeitamente para mim:

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

-> isso só funciona em dispositivo real.

  • Para simulador, você deve adicionar:
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)
}

Siga para a resposta de Stefan Arentz e Phenom.


Answer #4

Eu usei WKHTTPCookieStore em Objective-C, isso funcionou para mim para obter cookies persistentes e de sessão, mas só funciona no 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);
        }
 }];

Forçando o WKWebView a liberar seus dados internos, substituindo seu WKProcessPool, como descrito pela resposta de Stefan, funcionou para mim no iOS 10 e 11, mas apenas para cookies persistentes; parece que cookies de sessão são removidos, como J. Thoo descreveu


Answer #5

Não perca tempo na extração de cookies do iOS 11 below device , há muito menos chances de obter sucesso. A extração de cookies pode ser bloqueada por alguns motivos de segurança.

Consulte estes logs:

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

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

Experimente este código que é criado para dispositivos abaixo do iOS 11:

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

O código acima fornecerá um array de cookies vazio, já que a extração de cookies está sendo bloqueada devido a alguns motivos de segurança.

Eu recomendo que você tente seguir o que é para o iOS 11 e acima:

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

Answer #6

Na prática, eu encontrei no método de "decidePolicyForNavigationResponse", você pode usar o seguinte caminho para buscar cookies, mas o triste é que não é uma lista completa / inteira para uma sessão.

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

Answer #7

Para o iOS 11 , sem extensões:

 func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) { self.webView.configuration.websiteDataStore.httpCookieStore.getAllCookies { cookies in for cookie in cookies { //... } } } 

Answer #8

Esta postagem contém informações úteis sobre o tratamento de cookies com o WKWebView. De acordo com isso, você deve ser capaz de definir e recuperar cookies usando o padrão NSURLCache e NSHTTPCookie. Ele também se refere ao uso de WKProccessPool conforme o comentário de Stephan.


Answer #9
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];
      }];
}




wkwebview