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
- No olvides agregar el código de la solución aquí
- 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.
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)
}
Finalmente,
httpCookieStore
para
WKWebsiteDataStore
aterrizó en iOS 11.
https://developer.apple.com/documentation/webkit/wkwebsitedatastore?changes=latest_minor
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+
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];
}];
}