UIWebView轉WKWebView交互方法統一解決辦法

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已補上



作者:唉哦誒斯
鏈接:https://www.jianshu.com/p/b93d52f61673
來源:簡書
著作權歸作者所有。商業轉載請聯繫作者獲得授權,非商業轉載請註明出處。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章