undeclared type example ios swift uiwebview webkit wkwebview

ios - type - Obteniendo todas las cookies de WKWebView



wkwebview navigation swift (13)

mientras que obtener cookies de UIWebView parece sencillo al usar NSHTTPCookieStorage.sharedHTTPCookieStorage() , parece que WKWebView almacena las cookies en otro lugar.

Investigué un poco y pude obtener algunas cookies al NSHTTPURLResponse objeto NSHTTPURLResponse . esto, sin embargo, no contiene todas las cookies utilizadas por 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) } } } }

Curiosamente, también hay una clase WKWebsiteDataStore en ios 9 que se encarga de administrar las cookies en WKWebView , sin embargo, la clase no contiene un método público para recuperar los datos de las 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?? } } })

¿Hay alguna solución para obtener los datos de las cookies?


Detalles

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

Solución

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

Muestra completa

Info.plist

agregue su configuración de seguridad de transporte Info.plist

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

Código

  1. No olvides agregar el código de la solución aquí
  2. ViewController tiene un controlador de vista integrado

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


Como mencionó Stefan, las cookies se almacenan en NSHTTPCookieStorage.sharedHTTPCookieStorage()

Sin embargo, a partir de mis experimentos, descubrí que las cookies de sesión establecidas por el servidor no son visibles para NSHTTPCookieStorage.sharedHTTPCookieStorage() .

Siempre que cada WKWebView comparta la misma instancia de WKProcessPool , esas cookies de sesión se enviarán de vuelta al servidor para cada solicitud. Si cambia el grupo de procesos para un WKWebView , esencialmente está eliminando las cookies de sesión para todas las solicitudes futuras.


En NSHTTPCookie.cookiesWithResponseHeaderFields(headers, forURL: url) , ¿qué sucede si la url donde se configuran las cookies no es una url de respuesta de navegación (url que causa una navegación)? Noto que la URL de devolución de llamada donde se establecen las cookies nunca se llama en decidePolicyFor navigationResponse.

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

El delegado anterior nunca se ejecuta para la URL de devolución de llamada, ya que la devolución de llamada en sí no causa una navegación de página.

cookies(withResponseHeaderFields:for:)


En la práctica, encontré en el método de "decidePolicyForNavigationResponse", puedes usar la siguiente forma para recuperar cookies, pero lo triste es que no es una lista completa / completa para una sesión.

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



Las cookies utilizadas (creadas) por WKWebView se almacenan correctamente en NSHTTPCookieStorage.sharedHTTPCookieStorage() .

El problema es que WKWebView no WKWebView las cookies de inmediato. Creo que hace esto en su propio horario. Por ejemplo, cuando un WKWebView está cerrado o quizás periódicamente.

Entonces eventualmente terminan allí, pero cuando es impredecible.

Es posible que pueda forzar una ''sincronización'' al NSHTTPCookieStorage compartido cerrando su WKWebView . Por favor, háganos saber si esto funciona.

Actualización : Acabo de recordar que en Firefox para iOS WKWebView a WKWebView a vaciar sus datos internos, incluidas las cookies, al reemplazar su WKProcessPool por uno nuevo. No hay una API oficial, pero estoy bastante seguro de que es la solución más confiable en este momento.


No pierda su tiempo extrayendo cookies del iOS 11 below device , hay muy pocas posibilidades de tener éxito. La extracción de cookies puede bloquearse debido a razones de seguridad.

Consulte estos registros:

let response = navigationResponse.response as! NSHTTPURLResponse let headFields = response.allHeaderFields as! [String:String] let cookies = NSHTTPCookie.cookiesWithResponseHeaderFields(headFields, forURL: response.URL!)

Pruebe este código que está construido para dispositivos iOS 11 a continuación:

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

El código anterior le dará una matriz de cookies vacía, ya que las extracciones de cookies se están bloqueando debido a algunas razones de seguridad.

Te recomendaría que intentes seguir lo que está destinado a iOS 11 y superior:

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


Para iOS 11 , sin ninguna extensión:

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


Sé que esta es una pregunta muy antigua, y tenemos una solución, pero solo funciona en iOS 11 y superior. Para aquellos que están lidiando con iOS 10 y versiones anteriores (como yo), puede considerar este método. A mí me funciona perfectamente:

  • Proceso de reinicio forzado

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

-> esto solo funciona en dispositivos reales.

  • Para el simulador, debe agregar:

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

Sigue la respuesta de Stefan Arentz y Phenom.


Usé WKHTTPCookieStore en Objective-C, esto me funcionó para obtener cookies persistentes y de sesión, pero solo funciona en 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); } }];

Obligar a WKWebView a vaciar sus datos internos al reemplazar su WKProcessPool como se describe en la respuesta de Stefan, funcionó para mí en iOS 10 y 11, pero solo para las cookies persistentes; parece que las cookies de sesión se eliminan, como describió J. Thoo


Esta publicación tiene información útil sobre el manejo de cookies con WKWebView. De acuerdo con esto, debería poder configurar y recuperar cookies utilizando el estándar NSURLCache y NSHTTPCookie. También se refiere al uso de WKProccessPool según el comentario de Stephan.


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


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]; }]; }