三端易用的现代跨平台 JavaScript bridge -IOS篇

2018-02-08 10:25:26来源:https://juejin.im/post/5a7aaa9f6fb9a0634b4d6128作者:稀土掘金人点击

分享


DSBridge v3.0 是一个里程碑版本,和v2.0相比,有许多变化,需要注意的是v3.0不兼容
之前版本,但是我们也会继续维护v2.0分支,所以,如果你是v2.0的使用者,请放心继续使用v2.0,如果你是新用户,请使用>=v3.0.



DSBridge v3.0.0 更新列表



DSBridge-Android


特性
Android、IOS、Javascript 三端易用,轻量且强大、安全且健壮。
同时支持同步调用和异步调用
支持以类的方式集中统一管理API
支持API命名空间
支持调试模式
支持API存在性检测
支持进度回调:一次调用,多次返回
支持Javascript关闭页面事件回调
支持Javascript 模态/非模态对话框
Android端支持腾讯X5内核
安装
pod "dsBridge"
示例


请参考工程目录下的dsbridgedemo/
文件夹. 运行并查看示例交互.


如果要在你自己的项目中使用 dsBridge :


使用

新建一个类,实现API


@implementation JsApiTest
//同步API
- (NSString *) testSyn:(NSString *) msg
{
return [msg stringByAppendingString:@"[ syn call]"];
}
//异步API
- (void) testAsyn:(NSString *) msg :(void (^)(NSString * _Nullable result,BOOL complete))completionHandler
{
completionHandler([msg stringByAppendingString:@" [ asyn call]"],YES);
}
@end

可以看到,DSBridge正式通过API类的方式集中、统一地管理API。


添加API类实例到 DWKWebView


DWKWebView * dwebview=[[DWKWebView alloc] initWithFrame:bounds];
// register api object without namespace
[dwebview addJavascriptObject:[[JsApiTest alloc] init] namespace:nil];

在Javascript中调用原生 (Java/Object-c/swift) API ,并注册一个 javascript API供原生调用.



初始化 dsBridge


//cdn方式引入初始化代码(中国地区慢,建议下载到本地工程)
//<script src="https://unpkg.com/dsbridge@3.0.6/dist/dsbridge.js"> </script>
//npm方式安装初始化代码
//npm install dsbridge@3.0.6
var dsBridge=require("dsbridge")

调用原生API ,并注册一个 javascript API供原生调用.


//同步调用
var str=dsBridge.call("testSyn","testSyn");
//异步调用
dsBridge.call("testAsyn","testAsyn", function (v) {
alert(v);
})
//注册 javascript API
dsBridge.register('addValue',function(l,r){
return l+r;
})

在Object-c中调用Javascript API


[dwebview callHandler:@"addValue" arguments:@[@3,@4] completionHandler:^(NSNumber* value){
NSLog(@"%@",value);
}];
Object-C API signature

为了兼容Android,我们约定 OC API 签名如下:


同步API.



(id) handler:(id) msg



参数可以是任何类型, 但是返回值类型不能为void
.


异步 API.



(void) handler(id arg, (void (^)( id result,BOOL complete))completionHandler)



命名空间

命名空间可以帮助你更好的管理API,这在API数量多的时候非常实用,比如在混合应用中。DSBridge (>= v3.0.0) 支持你通过命名空间将API分类管理,并且命名空间支持多级的,不同级之间只需用'.' 分隔即可。


调试模式

在调试模式时,发生一些错误时,将会以弹窗形式提示,并且原生API如果触发异常将不会被自动捕获,因为在调试阶段应该将问题暴露出来。如果调试模式关闭,错误将不会弹窗,并且会自动捕获API触发的异常,防止crash。强烈建议在开发阶段开启调试模式,可以通过如下代码开启调试模式:


// open debug mode
[dwebview setDebugMode:true];
进度回调

通常情况下,调用一个方法结束后会返回一个结果,是一一对应的。但是有时会遇到一次调用需要多次返回的场景,比如在javascript钟调用端上的一个下载文件功能,端上在下载过程中会多次通知javascript进度, 然后javascript将进度信息展示在h5页面上,这是一个典型的一次调用,多次返回的场景,如果使用其它Javascript bridge, 你将会发现要实现这个功能会比较麻烦,而DSBridge本省支持进度回调,你可以非常简单方便的实现一次调用需要多次返回的场景,下面我们实现一个倒计时的例子:


In Object-c


- ( void )callProgress:(NSDictionary *) args :(void (^)(NSNumber * _Nullable result,BOOL complete))completionHandler
{
value=10;
hanlder=completionHandler;
timer =[NSTimer scheduledTimerWithTimeInterval:1.0
target:self
selector:@selector(onTimer:)
userInfo:nil
repeats:YES];
}
-(void)onTimer:t{
if(value!=-1){
hanlder([NSNumber numberWithInt:value--],NO);
}else{
hanlder(@"",YES);
[timer invalidate];
}
}

In javascript


dsBridge.call("callProgress", function (value) {
document.getElementById("progress").innerText = value
})

完整的示例代码请参考demo工程。


Javascript 弹出框


DSBridge已经实现了 Javascript的弹出框函数(alert/confirm/prompt),这些对话框按钮、标签文字默认都是中文的,如果你想自定义这些文本可以参考customJavascriptDialogLabelTitles
API,如果你不想使用DSBridge实现的对话框,你可以通过设置DSUIDelegate
属性(是WKUIDelegate的代理属性)完全自定义。



另外注意,DSBridge实现的弹出框都是模态的,这会阻塞UI线程,如果你需要非模态的对话框,请参考disableJavascriptDialogBlock
API.


API 列表
Object-C API


在Object-c中我们把实现了供 javascript调用的 API类的实例 成为Object-c API object
.


addJavascriptObject:(id) object namespace:(NSString *) namespace


添加一个 Object-c API object 到DWKWebView,并为它指定一个命名空间. 然后,在 javascript 中就可以通过bridge.call("namespace.api",...)
来调用Object-c API object中的原生API了。



如果命名空间是空(nil或空字符串), 那么这个添加的 Object-c API object就没有命名空间。在 javascript 通过bridge.call("api",...)
调用。


示例:


In Object-c
@implementation JsEchoApi
- (id) syn:(id) arg
{
return arg;
}
- (void) asyn: (id) arg :(void (^)( id _Nullable result,BOOL complete))completionHandler
{
completionHandler(arg,YES);
}
@end
// register api object with namespace "echo"
[dwebview addJavascriptObject:[[JsEchoApi alloc] init] namespace:@"echo"];
In Javascript
// call echo.syn
var ret=dsBridge.call("echo.syn",{msg:" I am echoSyn call", tag:1})
alert(JSON.stringify(ret))
// call echo.asyn
dsBridge.call("echo.asyn",{msg:" I am echoAsyn call",tag:2},function (ret) {
alert(JSON.stringify(ret));
})
removeJavascriptObject:(NSString *) namespace

通过命名空间名称移除相应的 Object-c API object.


callHandler:(NSString *) methodName arguments:(NSArray *) args

callHandler:(NSString *) methodName completionHandler:(void (^)(id value))completionHandler

callHandler:(NSString *) methodName arguments:(NSArray *) args completionHandler:(void (^ )(id value))completionHandler


调用 javascript API.methodName
为javascript API 的名称,可以包含命名空间;参数以数组传递,argumentss
数组中的元素依次对应javascript API的形参;completionHandler
用于接收javascript API的返回值,
注意:completionHandler
将在主线程中被执行


示例:


[dwebview callHandler:@"append" arguments:@[@"I",@"love",@"you"]
completionHandler:^(NSString * _Nullable value) {
NSLog(@"call succeed, append string is: %@",value);
}];
// call with namespace 'syn', More details to see the Demo project
[dwebview callHandler:@"syn.getInfo" completionHandler:^(NSDictionary * _Nullable value) {
NSLog(@"Namespace syn.getInfo: %@",value);
}];
disableJavascriptDialogBlock:(bool) disable


小心使用. 如果你再javascript中调用弹窗函数(alert
,confirm
, 或prompt
), 那么APP将会挂起,因为这些弹窗都是模态
的,会阻塞APP主线程,此时javascript执行流也会阻塞。如果你想避免阻塞,可以通过此API禁止,禁止后,一旦 javascript中调用了这些弹窗函数,APP将弹出非模态
对话框,并立即返回,(confirm
会返回true
,prompt
返回空字符串)。


如:


[dwebview disableJavascriptDialogBlock: true]


如果你想恢复模态
对话框,传false
调用即可.


setJavascriptCloseWindowListener:(void(^_Nullable)(void))callback


当 Javascript中调用window.close
时,DWKWebView会触发此监听器:


Example:


[dwebview setJavascriptCloseWindowListener:^{
NSLog(@"window.close called");
}];
hasJavascriptMethod:(NSString*) handlerName methodExistCallback:(void(^)(bool exist))callback


检测是否存在指定的 javascript API,handlerName
可以包含命名空间.


Example:


// test if javascript method exists.
[dwebview hasJavascriptMethod:@"addValue" methodExistCallback:^(bool exist) {
NSLog(@"method 'addValue' exist : %d",exist);
}];
setDebugMode:(bool) debug

设置调试模式。在调试模式时,发生一些错误时,将会以弹窗形式提示,并且原生API如果触发异常将不会被自动捕获,因为在调试阶段应该将问题暴露出来。如果调试模式关闭,错误将不会弹窗,并且会自动捕获API触发的异常,防止crash。强烈建议在开发阶段开启调试模式。


customJavascriptDialogLabelTitles:(NSDictionary*) dic

custom the label text of javascript dialog that includes alert/confirm/prompt, the default text language is Chinese.


自定义 javascript对话框上按钮、标签的文本,默认的文本语言是中文,你可以自定义英文,如:


[dwebview customJavascriptDialogLabelTitles:@{
@"alertTitle":@"Notification",
@"alertBtn":@"OK",
@"confirmTitle":@"",
@"confirmCancelBtn":@"CANCEL",
@"confirmOkBtn":@"OK",
@"promptCancelBtn":@"CANCEL",
@"promptOkBtn":@"OK"
}];
Javascript API
dsBridge

"dsBridge" 在初始化之后可用 .


dsBridge.call(method,[arg,callback])

同步或异步的调用Java API。



method
: Java API 名称, 可以包含命名空间。



arg
:传递给Java API 的参数。只能传一个,如果需要多个参数时,可以合并成一个json对象参数。



callback(String returnValue)
: 处理Java API的返回结果. 可选参数,只有异步调用时才需要提供
.


dsBridge.register(methodName|namespace,function|synApiObject)

dsBridge.registerAsyn(methodName|namespace,function|asyApiObject)

注册同步/异步的Javascript API. 这两个方法都有两种调用形式:


注册一个普通的方法,如:


In Javascript
dsBridge.register('addValue',function(l,r){
return l+r;
})
dsBridge.registerAsyn('append',function(arg1,arg2,arg3,responseCallback){
responseCallback(arg1+" "+arg2+" "+arg3);
})
In Object-c
// call javascript method
[dwebview callHandler:@"addValue" arguments:@[@3,@4] completionHandler:^(NSNumber * value){
NSLog(@"%@",value);
}];
[dwebview callHandler:@"append" arguments:@[@"I",@"love",@"you"] completionHandler:^(NSString * _Nullable value) {
NSLog(@"call succeed, append string is: %@",value);
}];


注册一个对象,指定一个命名空间:


In Javascript
//namespace test for synchronous
dsBridge.register("test",{
tag:"test",
test1:function(){
return this.tag+"1"
},
test2:function(){
return this.tag+"2"
}
})
//namespace test1 for asynchronous calls
dsBridge.registerAsyn("test1",{
tag:"test1",
test1:function(responseCallback){
return responseCallback(this.tag+"1")
},
test2:function(responseCallback){
return responseCallback(this.tag+"2")
}
})

因为Javascript并不支持函数重载,所以不能在同一个Javascript对象中定义同名的同步函数和异步函数


In Object-c
[dwebview callHandler:@"test.test1" completionHandler:^(NSString * _Nullable value) {
NSLog(@"Namespace test.test1: %@",value);
}];
[dwebview callHandler:@"test1.test1" completionHandler:^(NSString * _Nullable value) {
NSLog(@"Namespace test1.test1: %@",value);
}];

dsBridge.hasNativeMethod(handlerName,[type])


检测Java中是否存在名为handlerName
的API,handlerName
可以包含命名空间.



type
: 可选参数,["all"|"syn"|"asyn" ]
, 默认是 "all".


//检测是否存在一个名为'testAsyn'的API(无论同步还是异步)
dsBridge.hasNativeMethod('testAsyn')
//检测test命名空间下是否存在一个’testAsyn’的API
dsBridge.hasNativeMethod('test.testAsyn')
// 检测是否存在一个名为"testSyn"的异步API
dsBridge.hasNativeMethod('testSyn','asyn') //false
dsBridge.disableJavascriptDialogBlock(disable)


调用dsBridge.disableJavascriptDialogBlock(...)
和在Java中调用dwebview.disableJavascriptDialogBlock(...)
作用一样.


示例:


//disable
dsBridge.disableJavascriptDialogBlock()
//enable
dsBridge.disableJavascriptDialogBlock(false)
和 fly.js一起使用


当dsBridge遇见Fly.js
时,将会打开一个新的世界。fly.js传送门



正如我们所知,在浏览器中,ajax请求受同源策略限制,不能跨域请求资源。然而,Fly.js
有一个强大的功能就是支持请求重定向:将ajax请求通过任何Javascript bridge重定向到端上,并且Fly.js
官方已经提供的 dsBridge 的 adapter, 可以非常方便的协同dsBridge一起使用。由于端上没有同源策略的限制,所以 fly.js可以请求任何域的资源。



另一个典型的使用场景是在混合APP中,由于Fly.js
可以将所有ajax请求转发到端上,所以,开发者就可以在端上进行统一的请求管理、证书校验、cookie管理、访问控制等。



详情请参考github.com/wendux/fly
. (DSBridge Android版 demo中包含fly.js的示例)


最后

如果你瞎换 DSBridge, 欢迎star,以便更多的人知道它, 谢谢 !


最新文章

123

最新摄影

闪念基因

微信扫一扫

第七城市微信公众平台