美文网首页
我的WKWebView cookie处理方案

我的WKWebView cookie处理方案

作者: Beta是条好狗 | 来源:发表于2020-04-05 20:37 被阅读0次

Demo点这里

背景

正在维护的App使用cookie来维护用户登录状态、App版本系统、语言等状态信息。

在UIWebView时代,可以通过NSHTTPCookieStorage单例很直接的管理客户端cookie。UIWebView的cookie数据会自动和NSHTTPCookieStorage进行同步。然而WKWebView的cookie维护一直为人诟。只要你维护过相关业务,不同iOS版本上出现的各种cookie的问题一定让你头疼过。

这个Demo是目前项目中使用的cookie管理方案,方案来回折腾了好几个月,虽然不是很完整,但是基本满足当前项目需求。

方案

初始化时WKWebsiteDataStore使用[WKWebsiteDataStore defaultDataStore]WKProcessPool使用全局单例。

//  webView初始化
- (WKWebView *)wkWebView{
    if (!_wkWebView) {
        WKWebViewConfiguration * config = [[WKWebViewConfiguration alloc]init];
        config.allowsInlineMediaPlayback = YES;
        config.selectionGranularity = YES;
        config.processPool = [WebViewCookieUtil sharedProcessPool];
        config.userContentController = self.userContentController;
        
        _wkWebView = [[WKWebView alloc] initWithFrame:self.view.bounds configuration:config];
        _wkWebView.translatesAutoresizingMaskIntoConstraints = NO;
        _wkWebView.navigationDelegate = self;
        _wkWebView.UIDelegate = self;
        [_wkWebView addObserver:self forKeyPath:NSStringFromSelector(@selector(estimatedProgress)) options:0 context:nil];
        _wkWebView.opaque = NO;
        if (@available(iOS 11.0, *)) {
            _wkWebView.scrollView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
        }
    }
    return _wkWebView;
}

通过NSHTTPCookieStorage维护客户端cookie,当cookie更新时首先更新NSHTTPCookieStorage中,然后在不同的iOS版本使用不同的方式同步到WKWebView中。

+ (void)clientCookieDidUpdate:(NSDictionary *)cookieDict toRemove:(NSArray *)toRemove {
    //  客户端cookie更新时调用,比如用户登录状态改变
    NSHTTPCookieStorage *httpCookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
    // 移除Cookie
    NSArray<NSHTTPCookie *> *oldCookies = httpCookieStorage.cookies;
    [oldCookies enumerateObjectsUsingBlock:^(NSHTTPCookie * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        for (NSString *name in toRemove) {
            if ([obj.domain isEqualToString:name] && [obj.domain isEqualToString:domainForThisApp]) {
                [httpCookieStorage deleteCookie:obj];
            }
        }
    }];
    
    // Write new Cookie to storage.
    NSArray *cookieObjectArr = [self cookieObjectsFromCookieDict:cookieDict];
    for (NSHTTPCookie *cookie in cookieObjectArr) {
        [httpCookieStorage setCookie:cookie];
    }
    
    if (@available(iOS 11.0, *)) {
        WKHTTPCookieStore *wkCookieStore = [WKWebsiteDataStore defaultDataStore].httpCookieStore;
        [wkCookieStore getAllCookies:^(NSArray<NSHTTPCookie *> * _Nonnull currentCookies) {
            dispatch_group_t cleanGroup = dispatch_group_create();
            for (NSHTTPCookie *cookie in currentCookies) {
                for (NSString *name in toRemove) {
                    if ([cookie.domain isEqualToString:domainForThisApp] && [cookie.name isEqualToString:name]) {
                        dispatch_group_enter(cleanGroup);
                        [wkCookieStore deleteCookie:cookie completionHandler:^{
                            dispatch_group_leave(cleanGroup);
                        }];
                    }
                }
            }
            dispatch_group_notify(cleanGroup, dispatch_get_main_queue(), ^{
                for (NSHTTPCookie *cookie in cookieObjectArr) {
                    [wkCookieStore setCookie:cookie completionHandler:nil];
                }
            });
        }];
    } else {
        //  adjust userScript and reload webView if needed
    }
}

iOS11以上

管理方式如上段代码所示,在cookie更新之后直接对[WKWebsiteDataStore defaultDataStore]进行cookie设置即可。

潜在问题:
Cookie更新缓慢或无法更新。
当存在非视图结构中的WKWebView同时加载网页时,WKHttpCookieStorage的异步API回调可能会被阻塞导致cookie无法及时更新或者完全无法更新。
目前观察该问题出现在iOS11.3~iOS12.2的系统。如果遇到相同问题可以首先排查是否有多个webView同时加载。
如果解问题难以发现或者解决成本较高,可以牺牲性能使用nonPersistentDataStore来暂时规避这个问题

iOS10方案

iOS10或以下系统还没有提供httpCookieStorage,我们需要使用WKUserScript注入JS代码的方式进行cookie更新。

//  创建userContentController时添加UserScript
- (WKUserContentController *)userContentController {
    if (!_userContentController) {
        WKUserContentController *userContentController = [WKUserContentController new];
        if (@available(iOS 11.0, *)) {
            [userContentController addUserScript:[WebViewCookieUtil cookieScriptForIOS10AndEarlier]];
        }
        _userContentController = userContentController;
    }
    return _userContentController;
}

NSHTTPCookieStorage中的cookie转换成JS设置语句

//  WKCookieUtil
+ (WKUserScript *)cookieScriptForIOS10AndEarlier {
    NSMutableString *temp = @"".mutableCopy;
    NSHTTPCookieStorage *httpCookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
    [httpCookieStorage.cookies enumerateObjectsUsingBlock:^(NSHTTPCookie * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        if (![obj.domain isEqualToString:domainForThisApp]) {
            return ;
        }
        NSString *foo = [NSString stringWithFormat:@"%@=%@;domain=%@;path=/",obj.name, obj.value, domainForThisApp];
        [temp appendFormat:@"document.cookie = '%@';\n", foo];
    }];
    WKUserScript * cookieScript = [[WKUserScript alloc] initWithSource:[temp copy] injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:NO];
    return cookieScript;
}

即使已经添加WKUserScript,首个请求仍然需要通过设置request的httpHeader来带上cookie信息。

- (void)loadURLWithCachePolicy:(NSURLRequestCachePolicy)cachePolicy {
   //   ...
    if (@available(iOS 11.0, *)) {
        //  ...
    } else {
        NSString *cookieStr = [WebViewCookieUtil cookieStringForFirstRequestIOS10AndEarlier];
        NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:self.URL cachePolicy:cachePolicy timeoutInterval:10];
        [request addValue:cookieStr forHTTPHeaderField:@"Cookie"];
        [self.wkWebView loadRequest:request];
    }
}
//  WKCookieUtil
+ (NSString *)cookieStringForFirstRequestIOS10AndEarlier {
    NSHTTPCookieStorage *httpCookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
    
    return [self cookieStringForCurrentDomain:httpCookieStorage.cookies];
}

+ (NSString *)cookieStringForCurrentDomain:(NSArray<NSHTTPCookie *> *)cookies {
    NSMutableString *temp = @"".mutableCopy;
    [temp appendFormat:@"domain=%@; path=%@; ", domainForThisApp, @"/"];
    [cookies enumerateObjectsUsingBlock:^(NSHTTPCookie * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        if (![obj.domain isEqualToString:domainForThisApp]) {
            return ;
        }
        [temp appendFormat:@"%@=%@; ", obj.name, obj.value];
    }];
    return [temp copy];
}

补充:
如果当前webView使用过程中需要更新cookie,必须删除之前的WKUserScript,重新添加新的WKUserScript,然后reload整个页面。

参考资料

相关文章

  • 我的WKWebView cookie处理方案

    Demo点这里 背景 正在维护的App使用cookie来维护用户登录状态、App版本系统、语言等状态信息。 在UI...

  • 再会WKWebView

    有关WKWebView处理cookie问题,又多次测试,发现并不需要在 初识WKWebView那么麻烦,有更简...

  • WKWebView处理cookie问题

    问题描述 公司接入第三方H5接入,页面上需要输入用户名和密码,使用WKWebView总是记不住密码(应该是有时能记...

  • 关于WKWebView的Cookie处理

    WKWebView的坑相信大家已经踩过很多了.之前cookie的问题已经处理过一些了.但是这次又出现了新坑.在第一...

  • iOS WKWebView Cookie的处理

    1. 最近接到一个模块迁移的功能,就是把H5页面的某个功能模块嵌入的App中...其中涉及到一些原生和JS交互,c...

  • WKWebView对cookie的处理

    小编在网上查了很多wkwebview怎么去保存cookie,怎么保证cookie不丢失的资料,可大多发现说的不怎么...

  • WKWebView对Cookie的处理

    因为WKWebView是通过WebKit内核进行网络处理的,所以我们的NSHTTPCookieStorage里的C...

  • iOS开发-WKWebView设置cookie

    1.初始化WKWebView时设置cookie 2.WKWebView已经存在时,设置cookie

  • APP中的cookie

    wkwebview中的cookie: 1:wkwebview默认可以携带原生接口种下的cookie;iOS8以后,...

  • 疑难问题收集

    WKWebview 在重定向时 cookie 丢失 1.WKWebview 在重定向时 cookie 丢失: 1....

网友评论

      本文标题:我的WKWebView cookie处理方案

      本文链接:https://www.haomeiwen.com/subject/hsupphtx.html