React Native 与iOS的通信

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方法<TouchableHighlight     style={[styles.highLight,{marginTop:50}]}     underlayColor='#deb887'     activeOpacity={0.8}    onPress={() => this._nameAndAge()}    >    <Text>简单数据传递</Text></TouchableHighlight>_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端调用这个方法:

//为了测试方便我们先写个按钮<TouchableHighlight     style={styles.highLight}     underlayColor='coral'     activeOpacity={0.8}    onPress={() => this._dic()}    >    <Text>字典的传递和回调</Text></TouchableHighlight>//字典的传递和返回值    _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端是怎么调用的:

//作为测试的按钮<TouchableHighlight     style={styles.highLight}     underlayColor='#5f9ea0'     activeOpacity={0.8}    onPress={() => this._promise(30)}    >    <Text>Promise回调</Text></TouchableHighlight>//按钮的回调事件,将在这里调用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. */- (NSDictionary<NSString *, id> *)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];}

我们使用- (void)sendEventWithName:(NSString *)eventName body:(id)body 发送消息,eventName将是消息的名字,body可以传个字典作为消息体。 那么如何在js里面接受消息呢: 首先要import NativeEventEmitter,然后我们注册监听事件:

    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,源码下载

即使是不成熟的尝试,也胜于胎死腹中的策略。

React Native 与iOS的通信

相关文章:

你感兴趣的文章:

标签云: