iOS之WKWebView及时同步Cookies问题
WKWebView同步Cookies问题原因
之前在项目中使用WKWebView加载web网页时,出现Cookies在App端不同步的问题,在UIWebView中却是好用的。
项目曾经修复过一个版本,修复内容如下:
通过JS交互的方式解决的,实际上这样并不好,这样做不但即时性不好(问题还是会偶发),而且还浪费带宽。
js交互的方式虽然能解决大多数情况的同步问题,由于还是会偶发概率,需要彻底解决掉,现在找到原因了,在此记录一下加深记忆。
1、UIWebView同步问题
WKWebView使用的内核和UIWebView不同,在使用UIWebView时,在沙盒目录下会生成一个Library/Cookies文件夹。在Library/preference中有一个plist文件,从中也可以看到保存的Cookies。所以每次请求都会拿到cookies,不存在同步问题。
2、WKWebView同步问题
WKWebView则会在Library文件夹下生成一个WebKit文件夹,这里面都是只能看到文件夹,看不到Cookies。WKWebView 的 cookie 是单独保存在 WKWebView cookie 里的也就是WebKit文件下某一个文件,而不是我们平时用的 NSHTTPCookieStorage,所以这就会导致 cookie 不同步的问题。
WKWebView同步Cookies问题解决
所以我们需要在平时使用WKWebView时做一些处理,通常有三种方式:
拼接Cookies的方式
目前项目使用这种方式
开启请求
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[self.wkWebView loadRequest:request];
修改之后的代码
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
NSArray *cookies = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies];
NSDictionary *dict = [NSHTTPCookie requestHeaderFieldsWithCookies:cookies];
request.allHTTPHeaderFields = dict;
[self.wkWebView loadRequest:request];
问题点:
因为WKWebView的Cookies是WebKit的,和NSHTTPCookieStorage不是同步的,所以我们在加载网页的时候要拼接上去。
跨域代理问题解决方式:
同样为了解决跨域问题,在代理中每次跳转之前拼接Cookies
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
NSURL *url = navigationAction.request.URL;
[self wkWebViewWillStart:url];
//为了解决跨域问题,每次跳转url时把cookies拼接上
NSMutableURLRequest *request = (NSMutableURLRequest *)navigationAction.request;
NSArray *cookies = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies];
NSDictionary *dict = [NSHTTPCookie requestHeaderFieldsWithCookies:cookies];
request.allHTTPHeaderFields = dict;
decisionHandler(WKNavigationActionPolicyAllow);
}
注入js代码的方式
通过document.cookie同步到NSHTTPCookieStorage,简单方便。
通过注入JS解决Cookies不同步的问题:
[self.configuration.userContentController addUserScript:self.injectCookieScript];
- (WKUserScript *)injectCookieScript{
WKUserScript * cookieScript = [[WKUserScript alloc] initWithSource:[self cookieString] injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:NO];
return cookieScript;
}
- (NSString *)cookieString
{
NSMutableString *script = [NSMutableString string];
[script appendString:@"var cookieNames = document.cookie.split('; ').map(function(cookie) { return cookie.split('=')[0] } );\n"];
for (NSHTTPCookie *cookie in [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies]) {
if ([cookie.value rangeOfString:@"'"].location != NSNotFound) {
continue;
}
NSString *string = [NSString stringWithFormat:@"%@=%@;domain=%@;path=%@",
cookie.name,
cookie.value,
cookie.domain,
cookie.path ?: @"/"];
[script appendFormat:@"if (cookieNames.indexOf('%@') == -1) { document.cookie='%@'; };\n", cookie.name, string];
}
return script;
}
WKWebsiteDataStore
需要iOS11+
[WKWebsiteDataStore defaultDataStore].httpCookieStore;
它可以同步NSHTTPCookieStorage和WKWebView的Cookies,但是目前只支持iOS11.0以上的系统
在WKWebView中设置,意思是设置默认的CookieStore存储器.
WKWebView的cookie同步流程梳理
历史版本:
通过JS注入的方式,现在没有在用了,可能偶尔丢失cookie或同步不及时
现在的版本:
每次请求开启前,同步本地的接口请求下来的缓存cookie(检验时间,过期自动请求并缓存)
跨域处理的问题也有处理。