KZWFoudation系列之WKWebView的封装

2018 年 6 月 10 日 CocoaChina

在iOS 8.0以后苹果推出WKWebView,之前有性能问题的UIWebView基本就被弃用了,这里整理下我的WKWebView之旅和怎么封装的。


WKWebView有个绕不过去的问题就是Cookie.


我们先来看下Cookie到底是个什么东西:


简单地说,cookie 就是浏览器储存在用户电脑上的一小段文本文件。cookie 是纯文本格式,不包含任何可执行的代码。一个 Web 页面或服务器告知浏览器按照一定规范来储存这些信息,并在随后的请求中将这些信息发送至服务器,Web 服务器就可以使用这些信息来识别不同的用户。


大多数需要登录的网站在用户验证成功之后都会设置一个 cookie,只要这个 cookie 存在并可以,用户就可以自由浏览这个网站的任意页面。再次说明,cookie 只包含数据,就其本身而言并不有害。


紧跟 cookie 值后面的每个选项都以分号和空格分开,每个选择都指定了 cookie 在什么情况下应该被发送至服务器。第一个选项是过期时间(expires),指定了 cookie 何时不会再被发送至服务器,随后浏览器将删除该 cookie。该选项的值是一个 Wdy, DD-Mon-YYYY HH:MM:SS GMT 日期格式的值。


下一个选项是 domain,指定了 cookie 将要被发送至哪个或哪些域中。默认情况下,domain会被设置为创建该 cookie 的页面所在的域名,所以当给相同域名发送请求时该 cookie 会被发送至服务器。


另一个控制 Cookie 消息头发送时机的选项是 path 选项,和 domain 选项类似,path选项指定了请求的资源 URL 中必须存在指定的路径时,才会发送Cookie 消息头。这个比较通常是将 path 选项的值与请求的 URL 从头开始逐字符比较完成的。如果字符匹配,则发送 Cookie 消息头。


最后一个选项是 secure。不像其它选项,该选项只是一个标记而没有值。只有当一个请求通过 SSL 或 HTTPS 创建时,包含 secure 选项的 cookie 才能被发送至服务器。这种 cookie 的内容具有很高的价值,如果以纯文本形式传递很有可能被篡改。


UIWebView Cookie


同一个应用,不同UIWebView之间的Cookie是自动同步的。并且可以被其他网络类访问比如NSURLConnection,AFNetworking。


它们都是保存在NSHTTPCookieStorage容器中。 当UIWebView加载一个URL的时候,在加载完成时候,Http Response,对Cookie进行写入,更新或者删除,结果更新Cookie到NSHTTPCookieStorage存储容器中。


WKWebView Cookie


NSURLCache和NSHTTPCookieStroage无法操作(WKWebView)WebCore进程的缓存和Cookie。


WKWebView实例将会忽略任何的默认网络存储器(NSURLCache, NSHTTPCookieStorage, NSCredentialStorage) 和一些标准的自定义网络请求类(NSURLProtocol,等等.)。


WKWebView实例不会把Cookie存入到App标准的的Cookie容器(NSHTTPCookieStorage)中,因为 NSURLSession/NSURLConnection等网络请求使用NSHTTPCookieStorage进行访问Cookie,所以不能访问WKWebView的Cookie,现象就是WKWebView存了Cookie,其他的网络类如NSURLSession/NSURLConnection却看不到。,


与Cookie相同的情况就是WKWebView的缓存,凭据等。WKWebView都拥有自己的私有存储,因此和标准Cocoa网络类兼容的不是那么好。


你也不能自定义requests(增加自己的http header,更改已经存在的header)使用自定义的 URL schemes等等,因为NSURLProtocol也是不支持WKWebView的。


WKWebView Cookie 写入

1、JS注入:
WKUserContentController* userContentController = WKUserContentController.new;
   WKUserScript * cookieScript = [[WKUserScript alloc]
                                  initWithSource:[NSString stringWithFormat:@"document.cookie = '%@'", [self setCurrentCookie]]
                                  injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:NO];
   [userContentController addUserScript:cookieScript];
- (NSString *)setCurrentCookie {
   return @"";
}


2NSMutableURLRequest 注入
- (void)loadURLRequest {
   NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:self.url];
   [request addValue:[self readCurrentCookie] forHTTPHeaderField:@"Cookie"];
   [self.webView loadRequest:request];
}
- (NSString *)setCurrentCookie {
   return @"";
}


划重点:坑一:JS注入的Cookie,比如PHP代码在Cookie容器中取是取不到的, javascript document.cookie能读取到,浏览器中也能看到。


NSMutableURLRequest 注入的PHP等动态语言直接能从$_COOKIE对象中获取到,但是js读取不到,浏览器也看不到


所以合理的办法让js,php,浏览器都能读取到相同的Cookie方法就是创建WebView的时候javascript注入Cookie,一开始发送NSMutableURLRequest请求的时候也要加上Cookie,并且保证两个地方的设置的cookie一致。


坑二:WKWebView的cookie需要设置domain和path默认情况下会带进去不是通用的。(今天刚发现的)


坑三:网页登录跳原生之后登录成功后dimis后你需要重新注入和刷新把cookie塞进去,所以你的viewWillAppear每次都需要重新加载WKWebView和重新loadRequest,简直了。。。


KZWWebViewController 是如何做的呢?


我们知道,app里经常跳各种网页,我们不可能每个网页都去单独处理,所以我们写一个通用的可配置的KZWWebViewController,只需要传url进来就可以,其他你不要管了,是不是完美。


所以我们需要来设计一个这样的KZWWebViewController,首先必须的是跳转的url和其他的配置参数,然后是接入jsbridge,方便我们和网页的换下调用,这样我们的这个KZWWebViewController就基本满足需求了。


所以我的做法是抽出一个类来管理,它叫KZWRouterHelper,暴露一个方法

+ (void)pushbyPath:(NSString *)path xxx(xxx)xx .....


里面的操作是把你需要的配置的加上,然后转成字典塞入router

NSDictionary *params = @{
                            @"path": [path kzw_urlEncode],
                            @"timestimp":[NSString stringWithFormat:@"%g", [[NSDate date] timeIntervalSince1970]]
                            };
   NSString *url =
   [NSString stringWithFormat:@"%@?%@", KZWWebViewControllerRouterPath, [NSURL elm_queryStringFromParameters:params]];
   return [[ELMRouter sharedRouter] open:url animated:NO showStyle:ELMPageShowStylePush];


param里包含了你所以的配置,类如:

NSDictionary *params = @{
                            @"path": path,
                            @"fullScreen": @(NO),
                            @"fullUrl": @(NO),
                            @"title": string?string:@"",
                            @"timestimp":[NSString stringWithFormat:@"%g", [[NSDate date] timeIntervalSince1970]]
                            };


这个根据业务需求来配置就好,然后在controller里根据不同的参数做相应的处理就可以了,这样你的整个项目里所有的网页跳转就一行代码就好了:

[KZWRouterHelper pushbyURL:@"xxxx" ];


接入的jsbirdge最好是选择jscore的方式的,这样是同步,网页也可以加个配置方法,这个的主要目的,有的网页需要由网页自己来控制一些显示,原理同上我们自己的配置都是根据参数做不同的处理,具体看KZWWebViewController,然后很多时候产品想要跳二级页面的时候可以有2个返回,这时候如果你的leftBarButtonItems是统配的话最好在页面加载的时候就先设置一个返回,然后在didFinishNavigation代理设置成2个返回,一个返回上一个网页一个返回我们上一个控制器。这样做主要是你开始设置成统配后来直接配2个会闪一下。


还有就是WKWebView中的进度条,WKWebView的进度条比较简单你只要写一个UIProgressView然后监听WKWebView的加载进度就好了,然后title的显示也是监听就好了,如果没取到记得设置一个默认的。

[self.webView addObserver:self
                  forKeyPath:NSStringFromSelector(@selector(estimatedProgress))
                     options:0
                     context:nil];
   [self.webView addObserver:self forKeyPath:NSStringFromSelector(@selector(title)) options:NSKeyValueObservingOptionNew context:NULL];


然后是适配iPhone X的记得加这行代码:

if (KZW_iPhoneX) {
       self.webView.scrollView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
   }


最后记得释放你的监听:

- (NSString *)fullString:(NSString *)path {
   NSString *domain = nil;
   switch ([ELMEnvironmentManager environment]) {
       case ELMEnvBeta:
           domain = @"xxxxx";
           break;
       case ELMEnvAlpha:
           domain = @"xxxxx";
           break;
       case ELMEnvProduction:
           domain = @"xxxxx";
           break;
       default:
           domain = @"xxxxx";
           break;
   }
   if ([path containsString:@"http"]) {
       return path;
   }else {
      return [domain stringByAppendingString:path];
   }
}

- (void)dealloc {
   self.webView.UIDelegate = nil;
   [self.webView stopLoading];
   [self.webView removeObserver:self forKeyPath:NSStringFromSelector(@selector(estimatedProgress))];
   [self.webView removeObserver:self forKeyPath:NSStringFromSelector(@selector(title))];
   self.webView = nil;
}


组url的时候可以加个环境的配置。

就完啦,希望对你有用。具体看KZWFoudation中的KZWWebViewController,KZWRouterHelper和KZWDSJavaScripInterface。

https://github.com/ouyrp/KZWFoundation


哦还有一个cookie的清空和网页清缓存我也加上吧,前2篇关于WKWebView的文章就删了。

- (NSString *)readCurrentCookie {
   return @"";
}

- (NSString *)setCurrentCookie {
   return @"";
}
cookie清空只要这个2个方法里面参数清了就好了
if ([[UIDevice currentDevice].systemVersion floatValue] >= 9.0) {
       WKWebsiteDataStore *dateStore = [WKWebsiteDataStore defaultDataStore];
       [dateStore fetchDataRecordsOfTypes:[WKWebsiteDataStore allWebsiteDataTypes]
                        completionHandler:^(NSArray<WKWebsiteDataRecord *> * __nonnull records) {
                            for (WKWebsiteDataRecord *record  in records)
                            {
                                if ( [record.displayName containsString:@"xxxxx"])
                                {
                                    [[WKWebsiteDataStore defaultDataStore] removeDataOfTypes:record.dataTypes
                                                                              forDataRecords:@[record]
                                                                           completionHandler:^{
                                                                               NSLog(@"Cookies for %@ deleted successfully",record.displayName);
                                                                           }];
                                }
                            }
                        }];
   }else {
       NSString *librarypath = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES).firstObject;
       NSString *cookiesFolderPath = [librarypath stringByAppendingString:@"/Cookies"];
       [[NSFileManager defaultManager] removeItemAtPath:cookiesFolderPath error:nil];
   }
   NSHTTPCookieStorage *cookieJar = [NSHTTPCookieStorage sharedHTTPCookieStorage];
   for (NSHTTPCookie *cookie in [cookieJar cookies]) {
       [cookieJar deleteCookie:cookie];
   }
这是清缓存,亲测有效


作者:moonCoder

链接:https://www.jianshu.com/p/0f69030e9cb3


相关推荐:


登录查看更多
0

相关内容

Cookie(复数形态 Cookies)指某些网站为了辨别用户身份而储存在用户本地终端(Client Side)上的数据(通常经过加密)。定义于 RFC2109。
【ICML2020】多视角对比图表示学习,Contrastive Multi-View GRL
专知会员服务
79+阅读 · 2020年6月11日
还在修改博士论文?这份《博士论文写作技巧》为你指南
干净的数据:数据清洗入门与实践,204页pdf
专知会员服务
161+阅读 · 2020年5月14日
【实用书】Python爬虫Web抓取数据,第二版,306页pdf
专知会员服务
117+阅读 · 2020年5月10日
【干货书】流畅Python,766页pdf,中英文版
专知会员服务
225+阅读 · 2020年3月22日
【新书】Java企业微服务,Enterprise Java Microservices,272页pdf
【初学者系列】tensorboard学习笔记
专知
7+阅读 · 2019年10月4日
Python用于NLP :处理文本和PDF文件
Python程序员
4+阅读 · 2019年3月27日
从webview到flutter:详解iOS中的Web开发
前端之巅
5+阅读 · 2019年3月24日
Android P正式发布,你需要尽快做适配了
前端之巅
3+阅读 · 2018年8月7日
Python NLP入门教程
七月在线实验室
7+阅读 · 2018年6月5日
一个小例子带你轻松Keras图像分类入门
云栖社区
4+阅读 · 2018年1月24日
Python NLP 入门教程
大数据技术
19+阅读 · 2017年10月24日
Deep learning for cardiac image segmentation: A review
Arxiv
21+阅读 · 2019年11月9日
Arxiv
6+阅读 · 2019年7月29日
Semantics of Data Mining Services in Cloud Computing
Arxiv
4+阅读 · 2018年10月5日
Video-to-Video Synthesis
Arxiv
9+阅读 · 2018年8月20日
Arxiv
3+阅读 · 2018年2月22日
VIP会员
相关资讯
【初学者系列】tensorboard学习笔记
专知
7+阅读 · 2019年10月4日
Python用于NLP :处理文本和PDF文件
Python程序员
4+阅读 · 2019年3月27日
从webview到flutter:详解iOS中的Web开发
前端之巅
5+阅读 · 2019年3月24日
Android P正式发布,你需要尽快做适配了
前端之巅
3+阅读 · 2018年8月7日
Python NLP入门教程
七月在线实验室
7+阅读 · 2018年6月5日
一个小例子带你轻松Keras图像分类入门
云栖社区
4+阅读 · 2018年1月24日
Python NLP 入门教程
大数据技术
19+阅读 · 2017年10月24日
相关论文
Top
微信扫码咨询专知VIP会员