项目中大量使用了Hybrid开发,对native与js的交互做了个bridge,有需要的可以下载直接使用。使用Object-c语言,目前支持cocoapods导入。
pod 'GCWKWebViewJSBridge', '~> 0.1.0'
项目地址点击这里,有用的上的给个star吧
实现功能有:
- 实现了对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";
- 实现了对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调用JS函数时,web端未实现,此时可以打印出错误信息如下
js exception Message: ReferenceError: Can't find variable:
ocCallJS1 - URL: https://www.jianshu.com/ - Line: 1 - Column:
10 - Error object: {"line":1,"column":10,"sourceURL":
"https://www.jianshu.com/"}
- 对URL拦截交互,统一封装成block进行回调,集中管理。还涉及处理匹配优先级问题
在实际开发中可能会遇到拦截URL的时候,URL还需要给我们传递参数,这时候完全去匹配URL将匹配不到,只能进行包含kw进行匹配。block会返回keyURL和完整路径,我们可以通过处理完整路径获取参数。
实际还会遇到匹配URL时候,能够匹配到多个,这时候我们以完全匹配到,得分最高;匹配到的长度进行积分计算,然后返回积分最高的匹配。实际匹配中会出现
share:123
,share:12345
,share#
等,如果被拦截的链接是www.share12345.com/?
,这时候会计算出匹配到的是share12345
,在回掉中进行相关逻辑处理。
匹配规则如下:这里对匹配规则放在一个函数里,方便你根据业务可以对它进行扩展
- (NSString *)_machRuleWithUrl:(NSString *)URL{
NSString * matchedURL = @"";
//匹配积分,取匹配最高的
NSInteger currentMatchKeyScore = 0;
for (NSString * keyURL in self.interceptKeyURLArray) {
if ([keyURL isEqualToString:URL]){
//精准匹配到了,退出循环
matchedURL = keyURL;
currentMatchKeyScore = MaxMatchScore;
break;
}
//计算模糊匹配积分,以匹配到的keyURL长度计算积分,拦截得分最高的keyURL
if ([URL containsString:keyURL]) {
//以匹配到的字符长度为匹配分数
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传入参数,支持json对象,和字符对象传入;比如渲染网页前,需要拿到用户token等等
//oc向JS注入实例变量,可用来向h5注入用户token,信息等等
NSDictionary * userInfo = @{@"uid":@"10086",@"name":@"中国移动",@"age":@"22",@"token":@"oidahnfjabfiabfuaojfbaiufbafo"};
[bridge nativeUploadJSArguments:userInfo filedName:@"uoloadUser" inTime:WKUserScriptInjectionTimeAtDocumentStart];
*通过注入函数方式,向JS传入参数
//oc向JS注入参数,可用来向h5注入一个带参数返回值的函数,供h5调用
NSArray * lists = @[@"周1",@"周2",@"周3",@"周4"];
[bridge nativeUploadJSArguments:lists useMethod:@"getOCMessage" inTime:WKUserScriptInjectionTimeAtDocumentStart];
- 注册JS调用OC回调,通过messageName进行回调逻辑区分
//批量注册JS调用oc函数
[bridge registJSMethods:@[@"ocShare",@"getUserJson"] nativeHandler:^(NSString * _Nonnull messageName, id _Nonnull messageBody) {
NSLog(@"%@:%@",messageName,messageBody);
}];
- 注册OC调用JS函数,支持单参跟可变参数
if (button.tag == 1) {
//app调用js,带一个参数的,参数可以为字典和字符串
[bridge nativeCallJSMethod:@"ocCallJS" arguments:@"app调用JS成功" completionHandler:^(id _Nullable result, NSError * _Nullable error) {
}];
}else{
//app调用js,带多个可变参数,个数与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未实现OC需要调用的函数错误
- (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注入带返回值得函数,供JS获取native信息
/// @param param 变量值 可以是字符串,或者JSON对象
/// @param methodName js调用函数名
/// @param userScriptInjectionTime js注入时机
/**
前端使用:
Let x = window.【methodName()】
x为获得参数值
*/
- (void)nativeUploadJSArguments:(id)param useMethod:(NSString *)methodName inTime:(WKUserScriptInjectionTime)userScriptInjectionTime;
/// JS调用native
/// @param jsMethod js函数名称
/// @param nativehandler native响应的回调
/**
前端用法
window.webkit.messageHandlers.【jsMethod】.postMessage(【需要传给native的参数】)
*/
- (void)registJSMethod:(NSString *)jsMethod nativeHandler:(handler)nativehandler;
/// 批量注册
- (void)registJSMethods:(NSArray *)jsMethods nativeHandler:(handler)nativehandler;
/**
native 调用JS函数
@param methodName JS函数名
@param param 向JS传入参数,只支持一个参数,可以是字符串,或者JSON对象
@param completionHandler JS回调
前端:
实现 相应的method
*/
- (void)nativeCallJSMethod:(NSString *)methodName arguments:(id)param completionHandler:(void (^)(id _Nullable result, NSError * _Nullable))completionHandler;
/**
native 调用JS函数
@param methodName JS函数名
@param param 向JS传入多个参数,必须都为字符串形式,参数个数 与JS端保持一致,可以是字符串,或者JSON对象
@param completionHandler JS回调
前端:
实现 相应的method
*/
- (void)nativeCallJSMethod:(NSString *)methodName completionHandler:(void (^)(id _Nullable result, NSError * _Nullable))completionHandler arguments:(NSString *)param, ...;
- (void)removeAllUserScripts;
- (void)removeScriptMessageHandlerForName:(NSString *)method;
@end