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

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容