背景
iOS8后推出WKWebView,到现在已经iOS13,UIWebView即将不支持。
对比
UIWebView:
加载速度慢、内存占用多
WKWebView(苹果官方文档):
在性能、稳定性、功能方面有很大提升(高达60fps的滚动刷新率以及内置手势);
支持了更多的HTML5特性、JavaScript库;
将UIWebViewDelegate与UIWebView重构成了14个类与3个协议;
JS交互
关于JS交互这篇很详细
UIWebView:
Native->JS
通过调用执行JS代码的API实现:
NSString *title = [webView stringByEvaluatingJavaScriptFromString:@"document.title"];
复制代码JS-> Native
1、在shouldStartLoadWithRequest:代理方法中拦截信息,与前端协商好URL映射到原生方法。该回调在前端href或者iframe变化时触发。
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
NSString *url = request.URL.absoluteString;
if (url 字符串规则匹配) {
// 映射方法,执行Native逻辑
return NO;
}
return YES;
}
复制代码
2、通过JavaScriptCore注入
@protocol JSCallNativeFunc
-(void)dosomething
@end
self Class
//KVC得到WebView的JS上下文
JSContext *context = [webview valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
//给这个上下文注入JS对象
context[@"nativeObj"] = self;
//给这个上下文注入callNativeFunction函数当做JS对象
context[@"callNativeFunction"] = ^( JSValue * data )
{
//1 解读JS传过来的JSValue数据
//2 取出指令参数,确认要发起的native调用的指令
//3 取出数据参数,拿到JS传过来的数据
//4 根据指令调用对应的native方法,传递数据
//5 此时还可以将客户端的数据同步返回
}
-(void)dosomething{}
// js代码
nativeObj.dosomething()
复制代码
WKWebView:
Native->JS
通过调用执行JS代码的API实现:
[webView evaluateJavaScript:@"document.title"
completionHandler:^(id _Nullable title, NSError * _Nullable error) {
}];
复制代码JS-> Native
依靠WKScriptMessageHandler协议类和WKUserContentController两个类:
WKUserContentController对象负责注册JS方法,设置处理接收JS方法的代理,代理遵守WKScriptMessageHandler,实现捕捉到JS消息的回调方法
WKUserContentController * wkUController = [[WKUserContentController alloc] init];
[wkUController addScriptMessageHandler:self name:@"jsCallNativeFuncName"];
config.userContentController = wkUController;
#pragma mark - WKScriptMessageHandler
// 通过接收JS传出消息的name进行捕捉的回调方法
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message
{
// message.name ?= 'jsCallNativeFuncName'
SEL sel = NSSelectorFromString(message.name);
if ([_jsCallBack respondsToSelector:sel]) {
[_jsCallBack performSelector:sel];
} else {
NSLog(@"Not Found SEL:%@", message.name);
}
}
// 前端代码
window.webkit.messageHandlers.jsCallNativeFuncName.postMessage({});
复制代码
项目应用
针对我们工程现状的WK迁移方案总结
1.项目目录下扫描“UIWebview”,framework中的引用也可以检测出来
grep -r -n "UIWebView" ./
复制代码
2.API替换
初始化
config
delegate
对应API:
UIWebView
WKWebView
stringByEvaluatingJavaScriptFromString:
evaluateJavaScript: completionHandler:
shouldStartLoadWithRequest:
decidePolicyForNavigationAction:
3.兼容UIWebView中jsCallNative方法
通过js注入将WKWebView的js调用方式包装到UIWebView中原有的方法中,前端项目无需修改代码实现兼容
NSString *jsString = @"var nativeObj = new Object(); \
nativeObj.jsCallNativeFuncName = function(){ \
window.webkit.messageHandlers.jsCallNativeFuncName.postMessage({}); \
}";
WKUserScript *wkUScript = [[WKUserScript alloc] initWithSource:jsString injectionTime:WKUserScriptInjectionTimeAtDocumentEnd forMainFrameOnly:YES];
[WKUserContentController addUserScript:wkUScript];
复制代码
4.关于cookie
/**
我们支持iOS11+,所以可以通过WKHTTPCookieStore以Config给webView注入已存在NSHTTPCookieStorage中的cookie
*/
+ (WKWebViewConfiguration *)wkWebViewConfiguration
{
WKWebViewConfiguration *configuration = [WKWebViewConfiguration new];
if (@available(iOS 11.0, *)) {
WKHTTPCookieStore *cookieStore = configuration.websiteDataStore.httpCookieStore;
NSArray *cookies = [NSHTTPCookieStorage sharedHTTPCookieStorage].cookies;
for (NSHTTPCookie *cookie in cookies) {
[cookieStore setCookie:cookie completionHandler:nil];
}
}
return configuration;
}
复制代码
参考链接