WKWebView使用小感

前言

Apple 在iOS8上发布了WKWebView, 旨在替代UIWebView, 然后到目前为止在WKWebView的使用上依然有很多坑。本文主要记录一下最近关于WKWebView使用上的一些感想。欢迎读者留言并讨论,共同提高我们的开发效率。

WKWebView的优势

  • 相比较于UIWebView其更加高效,内存占有率更低。
  • 比较方便的实现滑动返回功能。
  • 在JS代码注入方法,特别是向Frame里面注入JS有较高的优势。

    基本使用方法

    关于WKWebView的使用这里不再一一细讲,网络上有很多关于它的使用教程,这个简要介绍一些相关注意点:

    进度条

    通过监听estimatedProgress可以实现进度条相关的功能。
    1
    [self.wkWebView addObserver:self forKeyPath:@"estimatedProgress" options:NSKeyValueObservingOptionNew context:nil];

退出ViewController之后网页音视频依然在播放

产生这个问题的原因是WebView在Vc退出之后并没有立即释放,我的解决方法是在viewDidDisappear方法里调用WKWebview的reload方法。

与JS交互

WKWebView与js交互相关包括:

  • 注入js
  • js调用native
  • native调用js

注入js

1
2
WKUserScript *us = [[WKUserScript alloc] initWithSource:@"js content text" injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:NO];
[configuration.userContentController addUserScript:us];

其中WKUserScriptInjectionTimeAtDocumentStart表示在Document加载之前就注入js, 第三个参数表示释放只在主frame里注入js。

js调用native

注册js方法

1
[self.webViewConfig.userContentController addScriptMessageHandler:self name:@"setUrlFilter"];

响应js方法

1
2
3
4
5
6
7
8
9
10
11
12
// 实现WKScriptMessageHandler 协议
-(void)userContentController:(WKUserContentController *)userContentController
didReceiveScriptMessage:(WKScriptMessage *)message {
if (StringEqualInsensitive(message.name, @"setUrlFilter")) {
[self setUrlFilter:message];
}
}
- (void)setUrlFilter:(WKScriptMessage *)message {
dispatch_async(dispatch_get_main_queue(), ^{
});
}

native调用js

1
2
[self.wkWebView evaluateJavaScript:@"window.getFavicon()" completionHandler:^(id _Nullable obj, NSError * _Nullable error) {
}];

自定义NSURLProtocol

首先让wkwebview支持NSURLProtocol可以参考这个库github
虽然作者通过hook的方式能实现拦截HTTP请求,但自定义NSURLProtocol缺造成了cookie丢失,post请求Body丢失的问题。对于一些简单的应用,可以通过产品上的一些方案避免这两个问题,但对于一些访问全网类应用,这两个问题却是致命的。

另外,有一种解决post,body丢失的方法是通过hook-ajax请求,让网页的请求通过客户端来方法。

Crash

我自己通过实现自定义NSURLProtocol来实现拦截特定的请求,但是在使用过程中发了频繁的崩溃,不知道是我使用有误还是框架原因,哪位读者朋友知道原因,希望不吝赐教。最后我放弃了自定义NSURLProtocol,崩溃日志如下:
崩溃信息
github上有一个相关的问题,将webView的ProcessPool设为单例,但这个做法并没有解决我的问题。相关连接webview crash

内容拦截

对于一些可访问全网信息的App,可能会涉及到拦截特定的网页或者广告,类似这样的需求在iOS11之前基本无法实现,因为通过实现NSURLProtocol来拦截指定的网页,会造成Cookie无法共享,POST方法Body丢失,以及莫名奇妙的Crash。但在iOS11之后,Apple开放了WKContentRuleListStore接口,我们可以使用它来限制不想访问的信息,如,广告。具体使用如下:

1
2
3
4
5
6
7
8
9
NSString *jsonPath = [[NSBundle mainBundle] pathForResource:@"contentRuleList" ofType:@"json"];
NSString *jsonString = [NSString stringWithContentsOfFile:jsonPath encoding:NSUTF8StringEncoding error:nil];
if (@available(iOS 11.0, *)) {
[[WKContentRuleListStore defaultStore] compileContentRuleListForIdentifier: @"qccastRemoveAd" encodedContentRuleList: jsonString completionHandler:^(WKContentRuleList *contentRuleList, NSError *error) {
if(error == nil) {
[self.wkWebView.configuration.userContentController addContentRuleList:rulelist];
}
}];
}