項目中大量使用了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