iOS13发布了,据说苹果开始拒绝使用UIWebView的api应用了。
有点慌,由于项目自17年开始就一直用的UIWebView,但UIWebView性能实在是太差了,进几个网页内存就飙升,并且退出页面VC销毁了但内存还无法降下来,首次加载网页的时候还会卡那么一会,只能弄个假进度条。
虽然中途也一直寻思着升级到WKWebView,但奈何项目中业务众多又是分布式的,和js交互的地方也很多,而WKWebView和UIWebView的交互方法写法又不一样,前端得区分是Android还是iOS,所有有交互的地方全要改,要前端配合改的话,估计人家也不乐意。
然后就这么一直拖着拖着,直到现在木有办法了,项目这时候刚好微信支付废掉了,要升级发新版本,跟钱相关的又刻不容缓,但又怕这次发新版由于UIWebView的问题被打回来,得硬着头皮上了,必须把这块硬石头给啃了。
说干就干
不要慌,一步一步来
先不管交互,先把UIWebView切换到WKWebView,看看只加载网页有没什么问题,再把网页加载完成之后的一些逻辑移植过来,再把进度条加上,OK,很完美,加载网页柔顺多了,内存也降下来了。
不要慌,接下来,集中精力搞定js和原生的交互
使用UIWebView时,js和原生交互是使用注册模型类,然后js再通过注册的模型类调用和原生声明好的交互方法。比如注册的模型名称为backJSAction
,交互方法为- (void)returnPage;
,那么js那边调用原生的就是backJSAction.returnPage()
而使用WKWebView时,js调用原生方法就变成了window.webkit.messageHandlers.backJSAction.postMessage();
,蛋疼就是在这里,前端得把以前的交互方式,全部改成这种,而Android那边却还是用上面的交互方式,这就得区分是Android还是iOS了(话说前端怎么区分呢?),就算改了,那旧版的APP就无法使用了,这无疑加大了工作量,并且代价有点高。
怎么办,怎么办,有没有一种优雅的方式,在不牵动前端和Android的情况下,顺利的将UIWebView切换到WKWebView。
不要慌,开始网上找资料
方法一、通过拦截url参数方式
需要iOS、Android、前端都改代码,不算严格意义上的js交互,而且交互方法无法返回值,直接pass。
方法二、使用第三方框架WebViewJavascriptBridge
此法也需要推翻重来,需要iOS、Android、前端都改代码,改动成本大,只能作为最后的补救方案。
就这样完了吗?不,肯定是我搜索的方式不对(我就纳闷了,这应该是很多人都会碰到的问题,为什么就找不到相关问题和解决办法)
不要慌,换个关键词继续找资料
终于,我仿佛看到了曙光,看到一种截然不同的解决办法
https://www.jianshu.com/p/afc52a5a28db
方法三、通过注入js脚本的方式,转换js的方法调用
说白了就是将在js调用backJSAction.returnPage()
方法时,将方法转换成window.webkit.messageHandlers.backJSAction.postMessage()
,这样就可以调到原生的交互方法了,顿时嘴角一扬。
不要慌,先写个小demo测试一下
测试了一个无返回值,无参数- (void)returnPage;
和有参数- (void)setPageTitle:(NSString *)title;
的方法,都很完美,能调用到,心情愉悦。
接着往下测试一个有返回值- (NSString *)getUserInfo;
,发现js无法接收到返回值,原来是window.webkit.messageHandlers.xxx
是没有返回值的,也就是WKWebView不支持返回值的交互方法,WTF?
就这样凉凉了?心中顿时跑过一万只草泥马。冷静片刻后......
不要慌,查查WKWebView怎么同步返回值。
看到的都是通过JS端调用prompt函数时,触发WKWebView的一个代理方法,在代理方法里原生可以同步返回值给js。
https://www.jianshu.com/p/5fc4c0c6fbdf
难道要前端把所有有返回值的方法调用,全部换成prompt函数?那这样还是要前端改代码,并且区分iOS和Android,这不是我的初衷,抓狂中.......
不行,头有点痛,休息一下
想啊想,想啊想,既然上面能将那么复杂的方法进行转化,那我是不是可以将js的方法也转换成prompt函数,APP再将返回值给prompt函数,再将prompt接收到的值返回给原始的js方法,有思路了就是干
prompt函数可以携带两个参数prompt和defaultText,就将原始js方法名当做prompt参数,传递到WKWebView的代理方法,APP就根据方法名来区分执行不同逻辑
脚本中的写法:
backJSAction.getUserInfo = function () {
var r = window.prompt("getUserInfo");
return r;
}
代理方法中写法:
#pragma mark ------ WKUIDelegate Delegate -------
// 交互。可输入的文本。
- (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * _Nullable))completionHandler {
NSLog(@"%@---%@",prompt,defaultText);
NSString *result = @"";
if ([prompt isEqualToString:@"getUserInfo"]) {
result = [self getUserInfo];
}else if ([prompt isEqualToString:@"getStrSign"]) {
result = [self getStrSign];
}
completionHandler(result);//这里就是要返回给JS的返回值
}
写完,测试,完全ojbk,js交互方法能接收APP返回的值。长吁一口气,貌似就这样完美解决了?哈哈哈哈,我真特么机智。
继续往下测试,当测试到这个交互方法时- (BOOL)isLogin;
,尼玛,又特么出问题了,代理方法的completionHandler()只能返回字符串类型,不能返回布尔值。WCCCCCCCCCCCCC,开始怀疑人生了。
哪怕前面解决了99%的问题,这个问题不解决,那就全白费了。
好在天无绝人之路,经过尝试,返回空字符串,js那边接收到的就是false,返回非空字符串时,js那边接收到的就是true,即想返回NO和YES,就分别completionHandler(@""),completionHandler(@"1")即可。
大功告成!六点了下班,放国庆假了,有时间再补个demo
10月15日
demo已补上