RN 可以很好的与原生进行交互,我们首先看看效果吧:
首先我们来看看 React Native 怎样调用 iOS 的代码并且带有简单的参数:
在 iOS 工程里面我们新建一个类 iOSExport,iOSExport 将会实现 RCTBridgeModule 协议。
首先我们要在 iOSExport 类的实现中添加这句宏定义:RCT_EXPORT_MODULE()
RCT_EXPORT_MODULE() 如果你不传入参数,那么你在 iOS 中导出的模块名就是类名,你也可以插入参数作为自定义模块名。
- @implementation iOSExport
- //定义导出的模块名
- RCT_EXPORT_MODULE()
- @end
后面我们就可以实现协议的代理方法了,协议方法的实现需要在 RCT_EXPORT_METHOD,这个宏里面。
我们先写一个有两个参数的方法给 js 调用:
- @implementation iOSExport
- //定义导出的模块名
- RCT_EXPORT_MODULE()
- //定义导出的方法名
- RCT_EXPORT_METHOD(rnToiOS:(NSString *)name :(NSInteger)age) {
- NSString *st = [NSString stringWithFormat:@"name:%@,age:%ld",name,age];
- NSLog(@"test:%@",st);
- [self alter:st];
- }
- @end
这样 OC 端的工作就 OK 了,下面我们继续看看 js 端怎么调用:
首先我们要在 js 文件里面 import NativeModules
然后在我们需要使用的时候获取导出的模块,我们再用模块调用 iOS 的导出的函数名就可以了,看代码:
- //创建一个可以点击的按钮,点击按钮后调用iOS的rnToiOS方法
- 50
- }]
- }
- underlayColor = '#deb887'activeOpacity = {
- 0.8
- }
- onPress = { () = >this._nameAndAge()
- } > 简单数据传递_nameAndAge() { //多参数的传递
- var iOSExport = NativeModules.iOSExport //获取到模块
- iOSExport.rnToiOS('帝君', 200) //直接调用函数
- this.setState({
- text: 'rnToiOS'
- })
- }
下面我们再看如何在 js 端调用 iOS 的含有字典参数和回调函数的方法。iOS 提供给 js 的回调函数是使用 block 实现的,看下回调函数的说明:
- /**
- * The type of a block that is capable of sending a response to a bridged
- * operation. Use this for returning callback methods to JS.
- */
- typedef void (^RCTResponseSenderBlock)(NSArray *response);
下面我们就可以用回调函数做参数,写一个我们需要的方法:
- RCT_EXPORT_METHOD(rnToiOSwithDic:(NSDictionary*)dic andCallback:(RCTResponseSenderBlock)callback) {
- NSMutableString *st = [NSMutableString string];
- for (NSObject *key in dic.allKeys) {
- NSString *string = [NSString stringWithFormat:@"%@:%@;",key,[dic objectForKey:key]];
- [st appendString:string];
- }
- callback(@[@"error",st]);
- [self alter:st];
- }
最终我们的回调函数给 js 的是一个数组,一般这个数组的第一个元素表示的都是错误。
看下如何在 js 端调用这个方法:
- //为了测试方便我们先写个按钮
- 'coral'
- activeOpacity={0.8}
- onPress={() => this._dic()}
- >
- 字典的传递和回调
- //字典的传递和返回值
- _dic() {
- var iOSExport = NativeModules.iOSExport //获取导出的模块
- iOSExport.rnToiOSwithDic({ //调用iOS的方法,第一个参数是字典
- '姓名':'幽冥',
- '年龄':20,
- '法力':'200'
- },(error,strings) =>{ //第二个参数是函数,做为回调函数给iOS将由iOS调用
- this.setState({
- text:strings
- })
- })
- this.setState({
- text:'rnToiOSwithDic'
- })
- }
上面对于回调函数的处理稍显麻烦,我们再看使用 promise 实现的回调函数:
我们先看下 iOS 里面的两个 block:
- /**
- * Block that bridge modules use to resolve the JS promise waiting for a result.
- * Nil results are supported and are converted to JS's undefined value.
- */
- typedef void (^RCTPromiseResolveBlock)(id result);
- /**
- * Block that bridge modules use to reject the JS promise waiting for a result.
- * The error may be nil but it is preferable to pass an NSError object for more
- * precise error messages.
- */
- typedef void (^RCTPromiseRejectBlock)(NSString *code, NSString *message, NSError *error);
这两个 block 其实就是实现 promise 回调的关键,一个是成功的回调一个是失败的回调,看下 iOS 端的实现:
- RCT_EXPORT_METHOD(rnToiOSAge:(NSInteger)age resolve:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
- if (age > 23) {
- resolve(@[@"句芒"]);
- }else {
- reject(@"101",@"年龄错误",[NSError errorWithDomain:@"错误" code:1 userInfo:nil]);
- }
- }
在 js 里面我要调用这个方法其实只要传一个参数 age 就可以了,后面两个参数可能会转换为 promis 作为返回值处理,看下 js 端是怎么调用的:
- //作为测试的按钮
- '#5f9ea0'
- activeOpacity={0.8}
- onPress={() => this._promise(30)}
- >
- Promise回调
- //按钮的回调事件,将在这里调用iOS的方法
- async _promise(age) { //Promise回调,异步执行
- try{
- var iOSExport = NativeModules.iOSExport
- var resolve = await iOSExport.rnToiOSAge(age) //执行iOS函数并且等待结果
- this.setState({
- text:resolve
- })
- }catch(e) {
- console.error(e);
- }
- }
这里要说下,因为我们是采用 promsie 返回值的方式处理回调的,所以我们不知道何时将返回结果,所以按钮的点击函数_promise(age) 前面要加个 async 关键字标识为异步函数,我们在函数里面可以使用 try{}catch(){} 来捕获异常,因为我们明确的知道如果 age 小于 24 就将报错,所以我们在这里一定要添加异常处理。注意了在执行 iOS 函数的时候也要加 await 关键字的,等待获取到返回值后再执行下面的操作。
在 iOS 里面也可以很方便地给 js 注入常量,采用如下方法可以方便的提供常量:
- /**
- * Injects constants into JS. These constants are made accessible via
- * NativeModules.ModuleName.X. It is only called once for the lifetime of the
- * bridge, so it is not suitable for returning dynamic values, but may be used
- * for long-lived values such as session keys, that are regenerated only as
- * part of a reload of the entire React application.
- */
- - (NSDictionaryid> *)constantsToExport;
这里要注意了,这函数只会在桥接的过程中执行一次,所以不太适合变量的传递,iOS 里面的实现:
- //为js提供静态数据
- - (NSDictionary < NSString * , id > *) constantsToExport {
- return@ {@"name": @"闲",
- @"age": @"22"
- };
- }
js 如何调用的:
- <TouchableHighlight
- style={styles.highLight}
- underlayColor='#5f9ea0'
- activeOpacity={0.8}
- onPress={() => this._getConst()}
- >
- <Text>获取iOS常量</Text>
- </TouchableHighlight>
- _getConst() {
- var iOSExport = NativeModules.iOSExport //获取模块
- this.setState({
- text:iOSExport.name+','+iOSExport.age //获取常量
- })
- }
在上面这些函数里面如果我想统一指定他们在什么线程里执行的,只要实现这函数就可以了:
- //告诉程序这个模块的代码在哪个线程执行
- - (dispatch_queue_t)methodQueue {
- return dispatch_get_main_queue(); //返回一个指定的线程
- }
前面说了那么多都是怎么在 js 里面调用 iOS 的方法的,那么如何让 iOS 主动发送消息给 js 呢。
首先我们更改一下 iOSExport 这个类,让它继承自 RCTEventEmitter,并且删除协议,因为 RCTEventEmitter 这个类也会实现 RCTBridgeModule 协议的:
- //继承自RCTEventEmitter的OC类将有资格给js发送消息
- @interface iOSExport :RCTEventEmitter
- @end
然后在 iOSExport 的实现里面我们还要重写一个方法,用来指定这模块将会发送哪些消息给 js :
- - (NSArray<NSString *> *)supportedEvents {
- return @[@"sendName"]; //这里返回的将是你要发送的消息名的数组。
- }
然后我们就可以直接发送消息了:
- - (void)alter:(NSString *)st {
- UIAlertController *alter = [UIAlertController alertControllerWithTitle:@"测试" message:st preferredStyle:UIAlertControllerStyleAlert];
- [alter addAction:[UIAlertAction actionWithTitle:@"了解" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
- //iOS发送通知给js
- [self sendEventWithName:@"sendName" body:@{@"name":@"江山",@"age":@"5000"}];
- }]];
- [[UIApplication sharedApplication].keyWindow.rootViewController presentViewController:alter animated:YES completion:nil];
- }
我们使用
发送消息,eventName 将是消息的名字,body 可以传个字典作为消息体。 那么如何在 js 里面接受消息呢:
- - (void)sendEventWithName:(NSString *)eventName body:(id)body
- componentWillMount() {
- //开始监听
- var iOSExport = NativeModules.iOSExport
- var emitter = new NativeEventEmitter(iOSExport) //用获取的模块创建监听器
- this.subScription = emitter.addListener("sendName", (body) = >this._getNotice(body)) //监听指定的事件,通过sendName这事件名来识别事件,(body) => this._getNotice(body)这是监听到事件后的处理方法,body 是iOS传过来的消息体
- }
- _getNotice(body) {
- this.setState({
- notice: body.name + ',
- ' + body.age
- })
- }
最后我们要注销监听:
- componentWillUnmount() {
- //删除监听
- this.subScription.remove()
- }
以上源码都已经上传 gitub,
来源: http://www.bubuko.com/infodetail-1870114.html