WKWebView之cookie的同步与管理
一、引言
iOS 11.0 之前,WKWebView 对于NSHTTPCookieStorage 的 Cookie 并不会每次都携带,对cookie管理一直是它的短板。iOS 11.0 推出WKHTTPCookieStore之后,WKWebView 对cookie 的管理改善了很多。基于H5对cookie的业务以来比较多。下文将介绍WKWebView cookie 管理。
二、iOS 11.0 之前cookie 捕获与注入
1. 捕获cookie
由于实现拦截协议时,WKNavigationDelegate 代理方法会失效,暂时不清楚什么原因,知道的伙伴告诉一下。以下是在 WKWebView的代理方法。
- WKNavigationDelegate捕获访问网站的cookie信息
- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler
{
//存储cookies
dispatch_sync(dispatch_get_global_queue(0, 0), ^{
NSHTTPURLResponse *response = (NSHTTPURLResponse *)navigationResponse.response;
NSArray *cookies =[NSHTTPCookie cookiesWithResponseHeaderFields:[response allHeaderFields] forURL:response.URL];
}
decisionHandler(WKNavigationResponsePolicyAllow);
}
2.NSURLRequest注入cookie
由于业务需求,WKWebView需要依赖cookie作登录状态校验,但是WKWebView并不依赖于NSHTTPCookieStorage。后来发现通过NSURLRequest注入cookie的方法可以有效解决该问题,但是通过NSURLRequest注入的方法依然无法使cookie长期有效。以下为通过NSURLRequest 注入cookie的方法。
注入cookie时我们需要兼容加载php和js两种形式(注意:插入cookie时必须在请求之前)。
- PHP携带cookie的形式
//通过host关联cookie。
[request setValue: [cookieSting appendString:
[NSString stringWithFormat:@"%@ = %@;",cookie.name,cookie.value]]
forHTTPHeaderField:@"Cookie"];
- JS携带cookie的形式
//关联cookie时host 关联。
WKUserScript * cookieScript = [[WKUserScript alloc] initWithSource:
[NSString stringWithFormat:@"document.cookie = '%@=%@';",cookie.name,cookie.value]
injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:NO];
[self.config.userContentController addUserScript: cookieScript];
三、iOS 11.0 cookie获取和插入
iOS11.0推出WKHTTPCookieStore后,cookie的管理将变得非常简单。WKHTTPCookieStore的使用和原理类似于NSHTTPCookieStorage;它将访问时获取的cookies自动存储在本地,下次访问时将自动携带相对应的cookie。
1. 获取cookie
//WKHTTPCookieStore的使用
WKHTTPCookieStore *cookieStore = self.webView.configuration.websiteDataStore.httpCookieStore;
//get cookies
[cookieStore getAllCookies:^(NSArray<NSHTTPCookie *> * _Nonnull cookies) {
NSLog(@"All cookies %@",cookies);
}];
2.注入cookie
//发送请求前插入cookie;
WKHTTPCookieStore *cookieStore = self.webiew.configuration.websiteDataStore.httpCookieStore;
[cookieStore setCookie:cookie completionHandler:nil];
四、cookie本地化管理
解决和cookie的捕获和插入的问题,那么cookie的本地化管理以及与NSHTTPCookieStorage 的cookie同步就迎刃而解了。
cookie本地化管理方案
方案解决的问题
- 兼容 iOS 11.0;
- NSHTTPCookieStorage 、WKHTTPCookieStore与本地磁盘cookies 的同步;
- 本地磁盘的管理;
- cookies的捕获和注入;
1. cookie的插入
- (void)insertCookie:(NSHTTPCookie *)cookie
{
@autoreleasepool {
if (@available(iOS 11.0, *)) {
WKHTTPCookieStore *cookieStore = self.configuration.websiteDataStore.httpCookieStore;
[cookieStore setCookie:cookie completionHandler:nil];
return;
}
NSMutableArray *TempCookies = [NSMutableArray array];
NSMutableArray *localCookies =[NSKeyedUnarchiver unarchiveObjectWithData:[[NSUserDefaults standardUserDefaults] objectForKey: PAWKCookiesKey]];
for (int i = 0; i < localCookies.count; i++) {
NSHTTPCookie *TempCookie = [localCookies objectAtIndex:i];
if ([cookie.name isEqualToString:TempCookie.name]) {
[localCookies removeObject:TempCookie];
i--;
break;
}
}
[TempCookies addObjectsFromArray:localCookies];
[TempCookies addObject:cookie];
NSData *cookiesData = [NSKeyedArchiver archivedDataWithRootObject: TempCookies];
[[NSUserDefaults standardUserDefaults] setObject:cookiesData forKey:PAWKCookiesKey];
[[NSUserDefaults standardUserDefaults] synchronize];
}
}
2.NSHTTPCookieStorage 和本地磁盘cookie的同步
- (NSMutableArray *)sharedHTTPCookieStorage
{
@autoreleasepool {
NSMutableArray *cookiesArr = [NSMutableArray array];
/** 获取NSHTTPCookieStorage cookies */
NSHTTPCookieStorage * shareCookie = [NSHTTPCookieStorage sharedHTTPCookieStorage];
for (NSHTTPCookie *cookie in shareCookie.cookies){
[cookiesArr addObject:cookie];
}
/** 获取自定义存储的cookies */
NSMutableArray *cookies = [NSKeyedUnarchiver unarchiveObjectWithData: [[NSUserDefaults standardUserDefaults] objectForKey: PAWKCookiesKey]];
//清除过期的cookies
for (int i = 0; i < cookies.count; i++) {
NSHTTPCookie *cookie = [cookies objectAtIndex:i];
if (!cookie.expiresDate) {
continue;
}
if ([cookie.expiresDate compare:self.currentTime]) {
[cookiesArr addObject:cookie];
}else
{
[cookies removeObject:cookie];
i--;
}
}
//存储最新的cookies
NSData *cookiesData = [NSKeyedArchiver archivedDataWithRootObject: cookies];
[[NSUserDefaults standardUserDefaults] setObject:cookiesData forKey:PAWKCookiesKey];
[[NSUserDefaults standardUserDefaults] synchronize];
return cookiesArr;
}
}
3.WKHTTPCookieStore Cookie同步
- (void)syncCookiesToWKHTTPCookieStore:(WKHTTPCookieStore *)cookieStore API_AVAILABLE(macosx(10.13), ios(11.0))
{
NSMutableArray *cookieArr = [self sharedHTTPCookieStorage];
if (cookieArr.count == 0)return;
for (NSHTTPCookie *cookie in cookieArr) {
[cookieStore setCookie:cookie completionHandler:nil];
}
}
4.清除Cookie
- (void)deleteAllWKCookies
{
if (@available(iOS 11.0, *)) {
NSSet *websiteDataTypes = [NSSet setWithObject:WKWebsiteDataTypeCookies];
NSDate *dateFrom = [NSDate dateWithTimeIntervalSince1970:0];
[[WKWebsiteDataStore defaultDataStore] removeDataOfTypes:websiteDataTypes modifiedSince:dateFrom completionHandler:nil];
}
NSData *cookiesData = [NSKeyedArchiver archivedDataWithRootObject: @[]];
[[NSUserDefaults standardUserDefaults] setObject:cookiesData forKey:PAWKCookiesKey];
[[NSUserDefaults standardUserDefaults] synchronize];
}