GCWKWebViewJSBridge 讓 iOS 原生與WKWebView交互更簡單

項目中大量使用了Hybrid開發(fā),對native與js的交互做了個bridge,有需要的可以下載直接使用。使用Object-c語言,目前支持cocoapods導(dǎo)入。

pod 'GCWKWebViewJSBridge', '~> 0.1.0'
項目地址點擊這里,有用的上的給個star吧

實現(xiàn)功能有:

  • 實現(xiàn)了對web控制臺打印的信息在xcode控制臺輸出
//注冊xcode控制臺 輸出web控制臺信息
    [bridge registCaptureJSConsoleLog];

簡書首頁 log輸出

js console log:{
    details =     {
        dns = 33;
        onload = 634;
        ttfb = 1269;
    };
    display = 1522;
    lifecycle =     {
        "_1" =         {
            desc = "\U7f51\U9875\U91cd\U5b9a\U5411\U7684\U8017\U65f6";
            key = redirect;
            value = 0;
        };
        "_2" =         {
            desc = "\U68c0\U67e5\U672c\U5730\U7f13\U5b58\U7684\U8017\U65f6";
            key = cache;
            value = 14;
        };
        "_3" =         {
            desc = "DNS\U67e5\U8be2\U7684\U8017\U65f6";
            key = dns;
            value = 33;
        };
        "_4" =         {
            desc = "TCP\U8fde\U63a5\U7684\U8017\U65f6";
            key = tcp;
            value = 464;
        };
        "_5" =         {
            desc = "\U5ba2\U6237\U7aef\U53d1\U8d77\U8bf7\U6c42\U7684\U8017\U65f6";
            key = request;
            value = 755;
        };
        "_6" =         {
            desc = "\U670d\U52a1\U7aef\U54cd\U5e94\U7684\U8017\U65f6";
  • 實現(xiàn)了對JS 異常的捕獲,輸出到控制臺
[bridge registCaptureJSExceptionLog];

存在跨域問題,只能收集到Script error.,打印不出詳細信息。
這個不是bug,webkik源碼判斷有跨域問題時候,為了安全,不提供具體錯誤信息

bool ScriptExecutionContext::sanitizeScriptError(String& errorMessage, int& lineNumber, String& sourceURL)
    {
        KURL targetURL = completeURL(sourceURL);
        if (securityOrigin()->canRequest(targetURL))
            return false;
        errorMessage = "Script error.";
        sourceURL = String();
        lineNumber = 0;
        return true;
    }

對于OC調(diào)用JS函數(shù)時,web端未實現(xiàn),此時可以打印出錯誤信息如下

js exception Message: ReferenceError: Can't find variable: 
ocCallJS1 - URL: http://m.itdecent.cn/ - Line: 1 - Column:
 10 - Error object: {"line":1,"column":10,"sourceURL":
"http://m.itdecent.cn/"}
  • 對URL攔截交互,統(tǒng)一封裝成block進行回調(diào),集中管理。還涉及處理匹配優(yōu)先級問題

在實際開發(fā)中可能會遇到攔截URL的時候,URL還需要給我們傳遞參數(shù),這時候完全去匹配URL將匹配不到,只能進行包含kw進行匹配。block會返回keyURL和完整路徑,我們可以通過處理完整路徑獲取參數(shù)。

實際還會遇到匹配URL時候,能夠匹配到多個,這時候我們以完全匹配到,得分最高;匹配到的長度進行積分計算,然后返回積分最高的匹配。實際匹配中會出現(xiàn) share:123,share:12345,share#等,如果被攔截的鏈接是www.share12345.com/?,這時候會計算出匹配到的是share12345,在回掉中進行相關(guān)邏輯處理。

匹配規(guī)則如下:這里對匹配規(guī)則放在一個函數(shù)里,方便你根據(jù)業(yè)務(wù)可以對它進行擴展

- (NSString *)_machRuleWithUrl:(NSString *)URL{
    NSString * matchedURL = @"";
    //匹配積分,取匹配最高的
    NSInteger currentMatchKeyScore = 0;
    for (NSString * keyURL in self.interceptKeyURLArray) {
        if ([keyURL isEqualToString:URL]){
            //精準匹配到了,退出循環(huán)
            matchedURL = keyURL;
            currentMatchKeyScore = MaxMatchScore;
            break;
        }
        //計算模糊匹配積分,以匹配到的keyURL長度計算積分,攔截得分最高的keyURL
        if ([URL containsString:keyURL]) {
            //以匹配到的字符長度為匹配分數(shù)
            NSInteger matchScore = keyURL.length;
            if (currentMatchKeyScore < matchScore) {
                matchedURL = keyURL;
                currentMatchKeyScore = matchScore;
            }
        }else continue;
    }
    return matchedURL;
}
//批量注冊攔截www.baidu.com
    [bridge registInterceptURLKeys:@[@"share:123",@"share:12345",@"share://info#"] handler:^(NSString * _Nonnull keyURL, NSString * _Nonnull URL) {
        NSLog(@"%@====\n%@",keyURL,URL);
    }];
  • 通過注入變量方式,向JS傳入?yún)?shù),支持json對象,和字符對象傳入;比如渲染網(wǎng)頁前,需要拿到用戶token等等
//oc向JS注入實例變量,可用來向h5注入用戶token,信息等等
    NSDictionary * userInfo = @{@"uid":@"10086",@"name":@"中國移動",@"age":@"22",@"token":@"oidahnfjabfiabfuaojfbaiufbafo"};
    [bridge nativeUploadJSArguments:userInfo filedName:@"uoloadUser" inTime:WKUserScriptInjectionTimeAtDocumentStart];

*通過注入函數(shù)方式,向JS傳入?yún)?shù)

//oc向JS注入?yún)?shù),可用來向h5注入一個帶參數(shù)返回值的函數(shù),供h5調(diào)用
    NSArray * lists = @[@"周1",@"周2",@"周3",@"周4"];
    [bridge nativeUploadJSArguments:lists useMethod:@"getOCMessage" inTime:WKUserScriptInjectionTimeAtDocumentStart];
  • 注冊JS調(diào)用OC回調(diào),通過messageName進行回調(diào)邏輯區(qū)分
 //批量注冊JS調(diào)用oc函數(shù)
    [bridge registJSMethods:@[@"ocShare",@"getUserJson"] nativeHandler:^(NSString * _Nonnull messageName, id  _Nonnull messageBody) {
         NSLog(@"%@:%@",messageName,messageBody);
    }];
  • 注冊O(shè)C調(diào)用JS函數(shù),支持單參跟可變參數(shù)
if (button.tag == 1) {
        //app調(diào)用js,帶一個參數(shù)的,參數(shù)可以為字典和字符串
        [bridge nativeCallJSMethod:@"ocCallJS" arguments:@"app調(diào)用JS成功" completionHandler:^(id  _Nullable result, NSError * _Nullable error) {
            
        }];
    }else{
         //app調(diào)用js,帶多個可變參數(shù),個數(shù)與JS端保持一致
        [bridge nativeCallJSMethod:@"ocCallJS1" completionHandler:^(id  _Nullable result, NSError * _Nullable error) {
            
        } arguments:@"我叫中國移動",@"今年1歲了",@"性別男",@"uid10086",nil];
    }
頭文件
@interface GCWKWebViewJSBridge : NSObject<WKScriptMessageHandler>
+ (instancetype)bridgeWithWKWebView:(WKWebView *)webView;
//注冊捕獲JS異常,跨域問題詳細信息可能捕獲不到;能捕獲到JS未實現(xiàn)OC需要調(diào)用的函數(shù)錯誤
- (void)registCaptureJSExceptionLog;
/// 注冊輸出web端控制臺信息
- (void)registCaptureJSConsoleLog;
- (void)registInterceptURLKeys:(NSArray *)keyUrls handler:(interceptURLHandler)handler;
- (void)registInterceptURLKey:(NSString *)keyURL handler:(interceptURLHandler)handler;
- (BOOL)webViewBridgeCanInterceptURL:(NSString *)URL;

/// native向H5注入js腳本
/// @param jsCode js代碼以字符串形式
/// @param userScriptInjectionTime 注入時機
- (void)registNativeUserScript:(NSString *)jsCode inTime:(WKUserScriptInjectionTime)userScriptInjectionTime;

/// 向JS注入一個全局變量,供JS使用
/// @param param  變量值,可以是字符串,或者JSON對象
/// @param filedName 變量名稱
/// @param userScriptInjectionTime 注入時機
/// 前端使用:取值即可
- (void)nativeUploadJSArguments:(id)param filedName:(NSString *)filedName inTime:(WKUserScriptInjectionTime)userScriptInjectionTime;

/// 向JS注入帶返回值得函數(shù),供JS獲取native信息
/// @param param 變量值 可以是字符串,或者JSON對象
/// @param methodName  js調(diào)用函數(shù)名
/// @param userScriptInjectionTime js注入時機
/**
 前端使用:
 Let x = window.【methodName()】
 x為獲得參數(shù)值
 */
- (void)nativeUploadJSArguments:(id)param useMethod:(NSString *)methodName inTime:(WKUserScriptInjectionTime)userScriptInjectionTime;

/// JS調(diào)用native
/// @param jsMethod js函數(shù)名稱
/// @param nativehandler native響應(yīng)的回調(diào)
/**
 前端用法
 window.webkit.messageHandlers.【jsMethod】.postMessage(【需要傳給native的參數(shù)】)
 */
- (void)registJSMethod:(NSString *)jsMethod nativeHandler:(handler)nativehandler;
/// 批量注冊
- (void)registJSMethods:(NSArray *)jsMethods nativeHandler:(handler)nativehandler;
/**
native 調(diào)用JS函數(shù)
@param methodName JS函數(shù)名
@param param 向JS傳入?yún)?shù),只支持一個參數(shù),可以是字符串,或者JSON對象
@param completionHandler JS回調(diào)

前端:
實現(xiàn) 相應(yīng)的method
*/
- (void)nativeCallJSMethod:(NSString *)methodName arguments:(id)param completionHandler:(void (^)(id _Nullable result, NSError * _Nullable))completionHandler;
/**
native 調(diào)用JS函數(shù)
@param methodName JS函數(shù)名
@param param 向JS傳入多個參數(shù),必須都為字符串形式,參數(shù)個數(shù) 與JS端保持一致,可以是字符串,或者JSON對象
@param completionHandler JS回調(diào)
前端:
實現(xiàn) 相應(yīng)的method
*/
- (void)nativeCallJSMethod:(NSString *)methodName completionHandler:(void (^)(id _Nullable result, NSError * _Nullable))completionHandler arguments:(NSString *)param, ...;

- (void)removeAllUserScripts;
- (void)removeScriptMessageHandlerForName:(NSString *)method;
@end
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容