這是兩個不同的項目的總結:
項目一骂际,只需多個webView之間共享cookie
項目二,在項目一的基礎上冈欢,增加了與native之間cookie的共享問題歉铝。
沒有耐心的同學,可以直接到文章末尾查看凑耻。
項目二中太示,共享cookie時,我最初沒有注意到cookie去重的問題香浩,導致加載時类缤,始終提示未登錄,仔細查找了問題弃衍,才發(fā)現(xiàn)是cookie重復呀非,并且最后一個cookie值為undefined導致的。
因項目一需求,需要在app中岸裙,初次加載首頁的webView時猖败,先行做webView的登錄,因為由首頁跳轉新的webView降允,打開新頁面是需要網(wǎng)頁上已經(jīng)登錄方可恩闻。
在使用了WKWebView后,這已經(jīng)是我第三次在嘗試處理這個問題了剧董,各種嘗試幢尚。
1.在loadRequest時,添加cookie信息
self.webRequest = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:urlStr]];
NSData *cookiesdata = [[NSUserDefaults standardUserDefaults] objectForKey:HHUserDefaultCookie];
if([cookiesdata length]) {
NSArray *cookies = [NSKeyedUnarchiver unarchiveObjectWithData:cookiesdata];
NSHTTPCookie *cookie;
for (cookie in cookies) {
NSString *cookieStr = [NSString stringWithFormat:@"%@=%@",cookie.name,cookie.value];
[self.webRequest addValue:cookieStr forHTTPHeaderField:@"Cookie"];
}
HHLog(@"請求時需要的Cookie %@",cookies);
}
[self.webView loadRequest:self.webRequest];
失敵崧ァ尉剩!
2.為webView的configuration設置cookie
HHWeakSelf(weakSlef);
WKUserContentController *userCC = self.webView.configuration.userContentController;
HHWKJSOCHandle *weakHandle = [[HHWKJSOCHandle alloc]initWithDelegate:weakSlef isActivity:NO];
[userCC addScriptMessageHandler:weakHandle name:HHNativeCategorymoreMethod];
[userCC addScriptMessageHandler:weakHandle name:HHNaviveCategoryMethod];
[userCC addScriptMessageHandler:weakHandle name:HHNativeProductMethod];
[userCC addScriptMessageHandler:weakHandle name:HHNativeActivityMethod];
// [userCC addScriptMessageHandler:weakSelf name:HHNativeShareMethod];
NSData *cookiesdata = [[NSUserDefaults standardUserDefaults] objectForKey:HHUserDefaultCookie];
NSArray *cookies = [NSKeyedUnarchiver unarchiveObjectWithData:cookiesdata];
NSHTTPCookie *cookie;
for (cookie in cookies) {
NSString *cookieStr = [NSString stringWithFormat:@"document.cookie = '%@=%@';",cookie.name,cookie.value];
WKUserScript * cookieScript = [[WKUserScript alloc] initWithSource: cookieStr injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:NO];
[userCC addUserScript:cookieScript];
}
失敗毅臊!
3.網(wǎng)頁加載完成后理茎,設置cookie
在- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation方法中
//取出cookie
NSHTTPCookieStorage *cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
//js函數(shù)
NSString *JSFuncString =
@"function setCookie(name,value,expires)\
{\
var oDate=new Date();\
oDate.setDate(oDate.getDate()+expires);\
document.cookie=name+'='+value+';expires='+oDate+';path=/'\
}\
function getCookie(name)\
{\
var arr = document.cookie.match(new RegExp('(^| )'+name+'=({FNXX==XXFN}*)(;|$)'));\
if(arr != null) return unescape(arr[2]); return null;\
}\
function delCookie(name)\
{\
var exp = new Date();\
exp.setTime(exp.getTime() - 1);\
var cval=getCookie(name);\
if(cval!=null) document.cookie= name + '='+cval+';expires='+exp.toGMTString();\
}";
//拼湊js字符串
NSMutableString *JSCookieString = JSFuncString.mutableCopy;
for (NSHTTPCookie *cookie in cookieStorage.cookies) {
NSString *excuteJSString = [NSString stringWithFormat:@"setCookie('%@', '%@', 1);", cookie.name, cookie.value];
[JSCookieString appendString:excuteJSString];
}
HHLog(@"JS字符串Cookie: %@",JSCookieString);
//執(zhí)行js
[webView evaluateJavaScript:JSCookieString completionHandler:^(id obj, NSError * _Nullable error) {
NSLog(@"執(zhí)行js %@",error);
}];
失敗管嬉!
包括這三種方式全部設置上皂林,依然失敗。
靜下心來思考蚯撩,確定出我的問題并不是讓WKWebView和Native共享cookie础倍,而是多個WKWebView之間如何共享cookie。
問題根源確定了胎挎,下一步就簡單多了沟启,只需要讓多個WKWebView共用同一個WKProcessPool實例就可以。
那么犹菇,如何能滿足WKWebView和Native共享cookie的同時美浦,并且使得多個WKWebView共用同一個WKProcessPool實例。
很簡單项栏,只要兩者相結合就好。
基于這個思路蹬竖,我們是不是可以先行讓webView加載一個網(wǎng)址沼沈,在加載之初就先行為webView的configuration設置cookie,如此,當?shù)谝粋€網(wǎng)址加載完畢之后币厕,webView之間就可以共享cookie了呢列另。
基于這個設想,來實現(xiàn)旦装。
使用示例页衙,在WKWebViewontroller中
- (WKWebView *)webView{
if (_webView == nil) {
WKUserContentController *userCC = [WKUserContentController new];
[userCC addScriptMessageHandler:self name:@"urlDirect"];
WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc]init];
//設置HTML5視頻是否允許網(wǎng)頁播放 設置為NO則會使用本地播放器
config.allowsInlineMediaPlayback = YES;
config.preferences.javaScriptCanOpenWindowsAutomatically = YES;
config.userContentController = userCC;
config.preferences.javaScriptEnabled = YES;
// config.suppressesIncrementalRendering = YES;
config.processPool = [HHWKCookieSyncManager sharedCookieManager].processPool;
_webView = [[WKWebView alloc]initWithFrame:ScreenBounds configuration:config];
_webView.backgroundColor = [UIColor whiteColor];
_webView.navigationDelegate = self;
_webView.UIDelegate = self;
_webView.scrollView.delegate = self;
[_webView sizeToFit];
if (@available(iOS 11.0, *)) {
_webView.scrollView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
}
[self.view addSubview:_webView];
[_webView mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.mas_equalTo(UIEdgeInsetsMake(0, 0, QSJSafeAreaBottomHeight, 0));
}];
}
return _webView;
}
注意關鍵代碼
config.processPool = [HHWKCookieSyncManager sharedCookieManager].processPool;
加載網(wǎng)絡請求時
- (void)refreshData{
if (self.errView) {
[self.errView removeFromSuperview];
}
//設置請求頭
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:self.url]];
if ([AccountManager shareInstance].isLogin) {
[request setValue:[QSJUserDefaults objectForKey:QSJUserID] forHTTPHeaderField:@"userId"];
[request setValue:[QSJUserDefaults objectForKey:QSJUserToken] forHTTPHeaderField:@"token"];
}
request.timeoutInterval = 10;
HHWKCookieSyncManager *cookieManager = [HHWKCookieSyncManager sharedCookieManager];
//這行代碼可以注釋
[cookieManager setWebViewCookie:self.webView];
if (@available(iOS 11.0, *)) {
[self.webView loadRequest:request];
}else{
@axc_weakify_self;
cookieManager.loadAction = ^{
@axc_strongify_self;
[self.webView loadRequest:request];
};
cookieManager.loadFailure = ^(NSString *errMsg) {
@axc_strongify_self;
[self createErrViewWithMsg:errMsg];
};
[cookieManager syncCookieForURL:[NSURL URLWithString:self.url] loadAction:cookieManager.loadAction];
}
}
下面是主要文件代碼
HHWKCookieSyncManager.h文件
//
// HHWKCookieSyncManager.h
// GlobalTimes
//
// Created by apple on 2017/4/11.
// Copyright ? 2017年 Hannah. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface HHWKCookieSyncManager : NSObject
@property (nonatomic, strong) WKProcessPool *processPool;
@property (nonatomic, strong) WKWebView *cookieWebview;
@property (nonatomic, copy) void(^loadAction)(void);
@property (nonatomic, copy) void(^loadFailure)(NSString *errMsg);
+ (instancetype)sharedCookieManager;
+ (NSString *)getCookieStr;
- (void)setWebViewCookie:(WKWebView *)webView;
- (void)syncCookieForURL:(NSURL *)url loadAction:(void(^)(void))loadAction;
- (void)shouldLoadRequestURL:(NSURL *)url scriptCallback:(void (^)(NSString *))scriptCallback ;
+ (void)removeCookieWithURL:(NSURL *)url;
@end
HHWKCookieSyncManager.m文件
//
// HHWKCookieSyncManager.m
// GlobalTimes
//
// Created by apple on 2017/4/11.
// Copyright ? 2017年 Hannah. All rights reserved.
//
#import "HHWKCookieSyncManager.h"
@interface HHWKCookieSyncManager ()<WKNavigationDelegate,WKUIDelegate>
@property (nonatomic, strong) WKWebView *webView;
@property (nonatomic, strong) NSURL *testUrl;
@end
@implementation HHWKCookieSyncManager
static inline WKUserScript * WKCookieUserScript(NSString *cookieString) {
if (!cookieString.length) {
return nil;
}
WKUserScript *cookieScript = [[WKUserScript alloc] initWithSource:cookieString
injectionTime:WKUserScriptInjectionTimeAtDocumentStart
forMainFrameOnly:NO];
return cookieScript;
}
+ (instancetype)sharedCookieManager{
static HHWKCookieSyncManager *__manager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
__manager = [[self alloc] init];
});
return __manager;
}
- (WKProcessPool *)processPool{
if (_processPool == nil) {
static dispatch_once_t onceToken;
static WKProcessPool *processPool;
dispatch_once(&onceToken, ^{
processPool = [[WKProcessPool alloc] init];
});
_processPool = processPool;
}
return _processPool;
}
- (WKWebView *)cookieWebview {
if (!_cookieWebview) {
WKUserContentController *userContentController = WKUserContentController.new;
WKWebViewConfiguration* webViewConfig = WKWebViewConfiguration.new;
webViewConfig.userContentController = userContentController;
webViewConfig.processPool = [HHWKCookieSyncManager sharedCookieManager].processPool;
_cookieWebview = [[WKWebView alloc] initWithFrame:CGRectZero configuration:webViewConfig];
_cookieWebview.UIDelegate = self;
_cookieWebview.navigationDelegate = self;
}
return _cookieWebview;
}
+ (NSString *)getCookieStr{
NSHTTPCookieStorage *cookieJar = [NSHTTPCookieStorage sharedHTTPCookieStorage];
NSMutableString *cookieString = [[NSMutableString alloc] init];
for (NSHTTPCookie *cookie in [cookieJar cookies]) {
//此處可進行cookie去重
if ([cookie.value isEqualToString:@"undefined"] == NO && cookie.value != NULL && cookie.value.length > 0) {
[cookieString appendFormat:@"document.cookie = '%@=%@';\n", cookie.name, cookie.value];
}
}
//刪除最后一個“;”
// [cookieString deleteCharactersInRange:NSMakeRange(cookieString.length - 1, 1)];
return [cookieString copy];
}
- (void)setWebViewCookie:(WKWebView *)webView{
NSHTTPCookieStorage *cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
for (NSHTTPCookie *cookie in cookieStorage.cookies) {
if (@available(iOS 11.0, *)) {
[webView.configuration.websiteDataStore.httpCookieStore setCookie:cookie completionHandler:nil];
} else {
// Fallback on earlier versions
NSString *cookieString = QSJFormat(@"document.cookie = '%@=%@';\n", cookie.name, cookie.value);
WKUserScript *script = [[WKUserScript alloc] initWithSource:cookieString injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:NO];
[webView.configuration.userContentController addUserScript:script];
}
}
}
- (void)syncCookieForURL:(NSURL *)url loadAction:(void(^)(void))loadAction {
[self shouldLoadRequestURL:url scriptCallback:^(NSString *cookieScript) {
if (cookieScript.length) {
[self.cookieWebview.configuration.userContentController removeAllUserScripts];
[self.cookieWebview.configuration.userContentController addUserScript:WKCookieUserScript(cookieScript)];
NSString *baseWebUrl = [NSString stringWithFormat:@"%@://%@", url.scheme,url.host];
//如果需要加載cookie,則需要再cookie webview加載結束后再加載url店乐,也就是在webView:(WKWebView *)webView didFinishNavigation方法中開始加載url
[self.cookieWebview loadHTMLString:@"" baseURL:[NSURL URLWithString:baseWebUrl]];
} else {
//如果沒有cookie需要加載艰躺,則直接加載url
if (loadAction) {
loadAction();
}
}
}];
}
- (void)shouldLoadRequestURL:(NSURL *)url scriptCallback:(void (^)(NSString *))scriptCallback {
if (!scriptCallback) {
return;
}
//此處可根據(jù)url決定是否需要加載cookie等邏輯
if (!url.host.length || [url.host isEqualToString:QSJHost] == NO) {
scriptCallback(nil);
return;
}
scriptCallback([HHWKCookieSyncManager getCookieStr]);
}
+ (void)removeCookieWithURL:(NSURL *)url{
if (@available(iOS 9.0, *)) {
NSSet *cookieTypeSet = [NSSet setWithObject:WKWebsiteDataTypeCookies];
[[WKWebsiteDataStore defaultDataStore] removeDataOfTypes:cookieTypeSet modifiedSince:[NSDate dateWithTimeIntervalSince1970:0] completionHandler:^{
}];
}
}
#pragma mark - WKNavigationDelegate
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation{
[webView evaluateJavaScript:[HHWKCookieSyncManager getCookieStr] completionHandler:^(id _Nullable response, NSError * _Nullable error) {
}];
if (self.loadAction) {
self.loadAction();
}
}
- (void)webView:(WKWebView *)webView didFailNavigation:(WKNavigation *)navigation withError:(NSError *)error{
if (error.code == NSURLErrorCancelled || error.domain == NSURLErrorDomain) {
return;
}
if (self.loadFailure) {
self.loadFailure(error.localizedDescription);
}
}
@end