WKWebView的cookie共享问题:与native之间、多webView之间

这是两个不同的项目的总结:
项目一,只需多个webView之间共享cookie
项目二,在项目一的基础上,增加了与native之间cookie的共享问题。
没有耐心的同学,可以直接到文章末尾查看。
项目二中,共享cookie时,我最初没有注意到cookie去重的问题,导致加载时,始终提示未登录,仔细查找了问题,才发现是cookie重复,并且最后一个cookie值为undefined导致的。

因项目一需求,需要在app中,初次加载首页的webView时,先行做webView的登录,因为由首页跳转新的webView,打开新页面是需要网页上已经登录方可。
在使用了WKWebView后,这已经是我第三次在尝试处理这个问题了,各种尝试。
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.网页加载完成后,设置cookie
在- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation方法中

  //取出cookie
    NSHTTPCookieStorage *cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
    //js函数
    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);
    //执行js
    [webView evaluateJavaScript:JSCookieString completionHandler:^(id obj, NSError * _Nullable error) {
        NSLog(@"执行js  %@",error);
    }];

失败!

包括这三种方式全部设置上,依然失败。
静下心来思考,确定出我的问题并不是让WKWebView和Native共享cookie,而是多个WKWebView之间如何共享cookie。
问题根源确定了,下一步就简单多了,只需要让多个WKWebView共用同一个WKProcessPool实例就可以。

那么,如何能满足WKWebView和Native共享cookie的同时,并且使得多个WKWebView共用同一个WKProcessPool实例。

很简单,只要两者相结合就好。

基于这个思路,我们是不是可以先行让webView加载一个网址,在加载之初就先行为webView的configuration设置cookie,如此,当第一个网址加载完毕之后,webView之间就可以共享cookie了呢。

基于这个设想,来实现。
使用示例,在WKWebViewontroller中

- (WKWebView *)webView{
    if (_webView == nil) {
        WKUserContentController *userCC = [WKUserContentController new];
        [userCC addScriptMessageHandler:self name:@"urlDirect"];
        
        WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc]init];
        //设置HTML5视频是否允许网页播放 设置为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;

加载网络请求时

- (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;
    }
    //此处可根据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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,968评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,601评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,220评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,416评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,425评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,144评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,432评论 3 401
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,088评论 0 261
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,586评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,028评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,137评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,783评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,343评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,333评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,559评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,595评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,901评论 2 345

推荐阅读更多精彩内容