Puis-je définir les cookies à utiliser par un WKWebView?

J'essaie de passer d'une application existante d'UIWebView à WKWebView. L'application actuelle gère la connection / session des users en dehors de la vue Web et définit les cookies requirejs pour l'authentification dans le NSHTTPCookieStore. Malheureusement, WKWebView n'utilise pas les cookies du NSHTTPCookieStorage. Y a-t-il un autre moyen d'y parvenir?

Si vous souhaitez que vos cookies soient définis sur la request de chargement initiale, vous pouvez les définir sur NSMutableURLRequest. Les cookies étant simplement un en-tête de requête spécialement formaté, ceci peut être réalisé comme suit:

WKWebView * webView = /*set up your webView*/ NSMutableURLRequest * request = [NSMutableURLRequest requestWithURL:[NSURL URLWithSsortingng:@"http://example.com/index.html"]]; [request addValue:@"TeskCookieKey1=TeskCookieValue1;TeskCookieKey2=TeskCookieValue2;" forHTTPHeaderField:@"Cookie"]; // use ssortingngWithFormat: in the above line to inject your values programmatically [webView loadRequest:request]; 

Si vous avez besoin des requêtes AJAX suivantes sur la page pour que leurs cookies soient définis, ceci peut être réalisé en utilisant simplement WKUserScript pour définir les valeurs par programmation via javascript au début du document comme ceci:

 WKUserContentController* userContentController = WKUserContentController.new; WKUserScript * cookieScript = [[WKUserScript alloc] initWithSource: @"document.cookie = 'TeskCookieKey1=TeskCookieValue1';document.cookie = 'TeskCookieKey2=TeskCookieValue2';" injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:NO]; // again, use ssortingngWithFormat: in the above line to inject your values programmatically [userContentController addUserScript:cookieScript]; WKWebViewConfiguration* webViewConfig = WKWebViewConfiguration.new; webViewConfig.userContentController = userContentController; WKWebView * webView = [[WKWebView alloc] initWithFrame:CGRectMake(/*set your values*/) configuration:webViewConfig]; 

La combinaison de ces deux techniques devrait vous donner assez d'outils pour transférer des valeurs de cookie de Native App Land à Web View Land. Vous pouvez find plus d'informations sur le cookie javascript api sur la page de mozilla , si vous avez besoin de cookies plus avancés.

Ouais, ça craint qu'Apple ne supporte pas la plupart des subtilités d'UIWebView . Je ne sais pas si ils les soutiendront jamais, mais j'espère qu'ils s'en sortiront bientôt. J'espère que cela t'aides!

Après avoir joué avec cette réponse (qui a été fantastique), nous avons dû faire quelques changements:

  • Nous avons besoin des vues Web pour gérer plusieurs domaines sans divulguer d'informations confidentielles sur les cookies entre ces domaines
  • Nous en avons besoin pour honorer les cookies sécurisés
  • Si le server modifie la valeur d'un cookie, nous voulons que notre application le sache dans NSHTTPCookieStorage
  • Si le server change une valeur de cookie, nous ne voulons pas que nos scripts la réinitialisent à sa valeur d'origine lorsque vous suivez un lien / AJAX etc.

Nous avons donc modifié notre code pour être ceci;

Créer une requête

 NSMutableURLRequest *request = [originalRequest mutableCopy]; NSSsortingng *validDomain = request.URL.host; const BOOL requestIsSecure = [request.URL.scheme isEqualToSsortingng:@"https"]; NSMutableArray *array = [NSMutableArray array]; for (NSHTTPCookie *cookie in [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies]) { // Don't even bother with values containing a `'` if ([cookie.name rangeOfSsortingng:@"'"].location != NSNotFound) { NSLog(@"Skipping %@ because it contains a '", cookie.properties); continue; } // Is the cookie for current domain? if (![cookie.domain hasSuffix:validDomain]) { NSLog(@"Skipping %@ (because not %@)", cookie.properties, validDomain); continue; } // Are we secure only? if (cookie.secure && !requestIsSecure) { NSLog(@"Skipping %@ (because %@ not secure)", cookie.properties, request.URL.absoluteSsortingng); continue; } NSSsortingng *value = [NSSsortingng ssortingngWithFormat:@"%@=%@", cookie.name, cookie.value]; [array addObject:value]; } NSSsortingng *header = [array componentsJoinedBySsortingng:@";"]; [request setValue:header forHTTPHeaderField:@"Cookie"]; // Now perform the request... 

Cela garantit que la première requête contient le jeu de cookies correct, sans envoyer de cookies du stockage partagé pour d'autres domaines, et sans envoyer de cookies sécurisés dans une request non sécurisée.

Traiter d'autres requests

Nous devons également nous assurer que les autres requests ont les cookies configurés. Ceci est fait en utilisant un script qui s'exécute sur le chargement de document qui vérifie s'il y a un set de cookies et si ce n'est pas le cas, définissez-le sur la valeur dans NSHTTPCookieStorage .

 // Get the currently set cookie names in javascriptland [script appendSsortingng:@"var cookieNames = document.cookie.split('; ').map(function(cookie) { return cookie.split('=')[0] } );\n"]; for (NSHTTPCookie *cookie in [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies]) { // Skip cookies that will break our script if ([cookie.value rangeOfSsortingng:@"'"].location != NSNotFound) { continue; } // Create a line that appends this cookie to the web view's document's cookies [script appendFormat:@"if (cookieNames.indexOf('%@') == -1) { document.cookie='%@'; };\n", cookie.name, cookie.wn_javascriptSsortingng]; } WKUserContentController *userContentController = [[WKUserContentController alloc] init]; WKUserScript *cookieInScript = [[WKUserScript alloc] initWithSource:script injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:NO]; [userContentController addUserScript:cookieInScript]; 

 // Create a config out of that userContentController and specify it when we create our web view. WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init]; config.userContentController = userContentController; self.webView = [[WKWebView alloc] initWithFrame:webView.bounds configuration:config]; 

Traiter les changements de cookies

Nous devons également gérer le changement de la valeur d'un cookie par le server. Cela signifie append un autre script à callbacker hors de la vue web que nous sums en NSHTTPCookieStorage créer pour mettre à jour NSHTTPCookieStorage .

 WKUserScript *cookieOutScript = [[WKUserScript alloc] initWithSource:@"window.webkit.messageHandlers.updateCookies.postMessage(document.cookie);" injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:NO]; [userContentController addUserScript:cookieOutScript]; [userContentController addScriptMessageHandler:webView name:@"updateCookies"]; 

et implémenter la méthode déléguée pour mettre à jour tous les cookies qui ont changé, en veillant à ne mettre à jour que les cookies du domaine actuel!

 - (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message { NSArray<NSSsortingng *> *cookies = [message.body componentsSeparatedBySsortingng:@"; "]; for (NSSsortingng *cookie in cookies) { // Get this cookie's name and value NSArray<NSSsortingng *> *comps = [cookie componentsSeparatedBySsortingng:@"="]; if (comps.count < 2) { continue; } // Get the cookie in shared storage with that name NSHTTPCookie *localCookie = nil; for (NSHTTPCookie *c in [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookiesForURL:self.wk_webView.URL]) { if ([c.name isEqualToSsortingng:comps[0]]) { localCookie = c; break; } } // If there is a cookie with a stale value, update it now. if (localCookie) { NSMutableDictionary *props = [localCookie.properties mutableCopy]; props[NSHTTPCookieValue] = comps[1]; NSHTTPCookie *updatedCookie = [NSHTTPCookie cookieWithProperties:props]; [[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookie:updatedCookie]; } } } 

Cela semble régler nos problèmes de cookies sans que nous ayons à traiter avec chaque endroit où nous utilisons WKWebView différemment. Nous pouvons maintenant utiliser ce code comme aide pour créer nos vues Web et il met NSHTTPCookieStorage jour de manière transparente pour nous.


EDIT: Il s'avère que j'ai utilisé une catégorie privée sur NSHTTPCookie – voici le code:

 - (NSSsortingng *)wn_javascriptSsortingng { NSSsortingng *ssortingng = [NSSsortingng ssortingngWithFormat:@"%@=%@;domain=%@;path=%@", self.name, self.value, self.domain, self.path ?: @"/"]; if (self.secure) { ssortingng = [ssortingng ssortingngByAppendingSsortingng:@";secure=true"]; } return ssortingng; } 

travaille pour moi

 func webView(webView: WKWebView, decidePolicyForNavigationAction navigationAction: WKNavigationAction, decisionHandler: (WKNavigationActionPolicy) -> Void) { let headerFields = navigationAction.request.allHTTPHeaderFields var headerIsPresent = contains(headerFields?.keys.array as! [Ssortingng], "Cookie") if headerIsPresent { decisionHandler(WKNavigationActionPolicy.Allow) } else { let req = NSMutableURLRequest(URL: navigationAction.request.URL!) let cookies = yourCookieData let values = NSHTTPCookie.requestHeaderFieldsWithCookies(cookies) req.allHTTPHeaderFields = values webView.loadRequest(req) decisionHandler(WKNavigationActionPolicy.Cancel) } } 

Voici ma version de la solution Mattrs dans Swift pour l'injection de tous les cookies de NSHTTPCookieStorage. Cela a été fait principalement pour injecter un cookie d'authentification pour créer une session user.

 public func setupWebView() { let userContentController = WKUserContentController() if let cookies = NSHTTPCookieStorage.sharedHTTPCookieStorage().cookies { let script = getJSCookiesSsortingng(cookies) let cookieScript = WKUserScript(source: script, injectionTime: WKUserScriptInjectionTime.AtDocumentStart, forMainFrameOnly: false) userContentController.addUserScript(cookieScript) } let webViewConfig = WKWebViewConfiguration() webViewConfig.userContentController = userContentController self.webView = WKWebView(frame: self.webViewContainer.bounds, configuration: webViewConfig) } ///Generates script to create given cookies public func getJSCookiesSsortingng(cookies: [NSHTTPCookie]) -> Ssortingng { var result = "" let dateFormatter = NSDateFormatter() dateFormatter.timeZone = NSTimeZone(abbreviation: "UTC") dateFormatter.dateFormat = "EEE, d MMM yyyy HH:mm:ss zzz" for cookie in cookies { result += "document.cookie='\(cookie.name)=\(cookie.value); domain=\(cookie.domain); path=\(cookie.path); " if let date = cookie.expiresDate { result += "expires=\(dateFormatter.ssortingngFromDate(date)); " } if (cookie.secure) { result += "secure; " } result += "'; " } return result } 

mettre cookie

 self.webView.evaluateJavaScript("document.cookie='access_token=your token';domain='your domain';") { (data, error) -> Void in self.webView.reload() } 

supprimer le cookie

 self.webView.evaluateJavaScript("document.cookie='access_token=';domain='your domain';") { (data, error) -> Void in self.webView.reload() } 

Swift 3 mise à jour:

  func webView(_ webView: WKWebView, decidePolicyFor navigationResponse: WKNavigationResponse, decisionHandler: @escaping (WKNavigationResponsePolicy) -> Void) { if let urlResponse = navigationResponse.response as? HTTPURLResponse, let url = urlResponse.url, let allHeaderFields = urlResponse.allHeaderFields as? [Ssortingng : Ssortingng] { let cookies = HTTPCookie.cookies(withResponseHeaderFields: allHeaderFields, for: url) HTTPCookieStorage.shared.setCookies(cookies , for: urlResponse.url!, mainDocumentURL: nil) decisionHandler(.allow) } } 

Dans iOS 11, vous pouvez gérer le cookie maintenant :), voir cette session: https://developer.apple.com/videos/play/wwdc2017/220/

entrez la description de l'image ici

S'il vous plaît find la solution qui fonctionnera le plus probablement pour vous hors de la boîte. Fondamentalement, il est modifié et mis à jour pour la réponse de Swift 4 @ user3589213 .

 func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) { let headerKeys = navigationAction.request.allHTTPHeaderFields?.keys let hasCookies = headerKeys?.contains("Cookie") ?? false if hasCookies { decisionHandler(.allow) } else { let cookies = HTTPCookie.requestHeaderFields(with: HTTPCookieStorage.shared.cookies ?? []) var headers = navigationAction.request.allHTTPHeaderFields ?? [:] headers += cookies var req = navigationAction.request req.allHTTPHeaderFields = headers webView.load(req) decisionHandler(.cancel) } }