RN can interact well with the native, so let's look at the effect first:
First, let's take a look at react Native how to invoke iOS code with simple parameters:
In iOS project we create a new class Iosexport,iosexport will implement the Rctbridgemodule protocol.
First we will add this macro definition to the implementation of the Iosexport class: Rct_export_module ()
Rct_export_module () If you don't pass in the parameter, the module name you exported in iOS is the class name, and you can insert the parameter as a custom module name.
@implementation iOSExport//定义导出的模块名RCT_EXPORT_MODULE()@end
Later we can implement the Proxy method of the Protocol, the implementation of the Protocol method needs to be in the Rct_export_method, the macro inside.
Let's start by writing a method with two arguments to the JS call:
@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
So the OC end of the work is OK, below we continue to look at the JS side how to invoke:
First we want to import nativemodules in JS file
Then we get the exported module when we need to use it, and then we use the module to call the exported function name of iOS, see the code:
//create a button that can be clicked, click the button and call the iOS Rntoios method <touchablehighlight style={[styles.highlight,{margintop:50 }]} Underlaycolor= ' #deb887 ' activeopacity={0.8 } onpress={() = this . _nameandage ()} > <Text> Simple Data transfer </text></ Touchablehighlight>_nameandage () {//multi-parameter delivery var
iosexport = nativemodules.iosexport //get to Module iosexport.rntoios ( ' Emperor Gentleman ' , 200 ) //direct call function this . SetState ({text: ' Rntoios ' })}
pre>
Let's look at how to invoke iOS's method with dictionary parameters and callback functions on the JS side. The callback function provided by iOS to JS is implemented using block, and the description of the callback function is:
/** * The type of a block that is capable of sending a response to a bridged * operation. Use this for returning callback methods to JS. */void (^RCTResponseSenderBlock)(NSArray *response);
Here we can use the callback function to do the argument, write a method we need:
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];}
Finally our callback function to the JS is an array, generally the first element of this array represents an error.
See how to invoke this method on the JS side:
//For testing convenience we'll write a button <touchablehighlight Style={styles.highlight} underlaycolor=' Coral 'activeopacity={0.8} onpress={ () = This. _dic ()} > <Text> dictionary delivery and callbacks </Text></TouchableHighlight>//The dictionary's delivery and return value _dic () {varIosexport = Nativemodules.iosexport//Gets the exported module iosexport.rntoioswithdic ({//Call the iOS method, the first parameter is a dictionary' name ':' The Nether ',' age ': -,' Mana ':' $ '}, (error,strings) ={//The second argument is a function, which is called by iOS as a callback function This. setState ({text: Strings})}) This. setState ({text:' Rntoioswithdic '}) }
It's a little cumbersome to handle the callback function above, and we'll look at the callback function implemented with promise:
Let's look at the two blocks in iOS:
/** * 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. */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. */void (^RCTPromiseRejectBlock)(NSString *code, NSString *message, NSError *error);
These two blocks are really the key to implementing the promise callback, one of which is a successful callback, a failed callback, and a look at the iOS implementation:
RCT_EXPORT_METHOD(rnToiOSAge:(NSInteger)age resolve:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) { if23) { resolve(@[@"句芒"]); }else { reject(@"101",@"年龄错误",[NSError errorWithDomain:@"错误" code:1 userInfo:nil]); }}
In JS I want to call this method in fact, just pass a parameter of age can be, the following two parameters may be converted to Promis as the return value processing, see how the JS side is called:
//As the test button<touchablehighlight Style={styles.highlight} underlaycolor=' #5f9ea0 'activeopacity={0.8} onpress={() = This. _promise ( -)} > <text>promise callback </Text></TouchableHighlight>//Button callback event that will call the iOS method hereAsync_promise (age) {//promise callback, asynchronous execution Try{varIosexport = Nativemodules.iosexportvarResolve =awaitIosexport.rntoiosage (age)//Execute iOS functions and wait for results This. setState ({text:resolve})}Catch(e) {console.error (e); }}
This is to say, because we handle callbacks in a Promsie return value, so we don't know when the results will be returned, so the button's click Function _promise (age) is preceded by an async keyword identified as an async function, and we can use try{} in the function. catch () {} to catch the exception, because we know explicitly that if age is less than 24 will be an error, so we must add exception handling here. Note that when you execute an iOS function, you also add the await keyword, wait for the return value, and then perform the following operation.
In iOS can also be easily injected into JS constant, using the following methods can be easily provided constants:
/** * Injects constants intoMsi These constants is made accessible via * nativemodules.modulename.x. It isOnly called once for theLifetime of the* Bridge, soit is notSuitable for returningDynamic values, butMay is used * forLong-lived values such assession keys, thatis regenerated only as* Part ofA reload of theEntire ReactApplication. */-(nsdictionary<nsstring *,ID> *) constantstoexport;
Note here that this function will only be executed once during the bridging process, so it is not quite suitable for variable delivery, the implementation of iOS:
//为js提供静态数据- (NSDictionary<NSString *,id> *)constantsToExport { return @{@"name":@"闲",@"age":@"22"};}
How JS is called:
<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 //获取常量 }) }
In the above functions if I want to uniformly specify what thread they are executing on, just implement this function:
//告诉程序这个模块的代码在哪个线程执行- (dispatch_queue_t)methodQueue { return//返回一个指定的线程}
The front said so much is how to invoke the iOS method in JS, then how to let iOS actively send a message to JS it.
First we change the Iosexport class, let it inherit from Rcteventemitter, and delete the protocol, because rcteventemitter this class will also implement the Rctbridgemodule protocol:
//继承自RCTEventEmitter的OC类将有资格给js发送消息@interface iOSExport :RCTEventEmitter@end
Then in the implementation of Iosexport we also have to rewrite a method to specify which messages this module will send to JS:
- (NSArray<NSString *> *)supportedEvents { return @[@"sendName"//这里返回的将是你要发送的消息名的数组。
Then we can send the message directly:
-(void) Alter: (NSString *) St {Uialertcontroller * Alter = [Uialertcontroller alertcontrollerwithtitle:@"test" message:st Preferredstyle: Uialertcontrollerstylealert];[ alter addaction:[uialertaction actionwithtitle:@"Understanding" style:uialertactionstylecancel Handler: ^ (uialertaction * _nonnull action) {//ios send notification to JS [self sendeventwithname:@"Sendname" body:@{@"Name": @ "Jiangshan", @ "Age": @"the"}];}]]; [[UIApplication Sharedapplication].keywindow.rootviewcontroller Presentviewcontroller: alter Animated:yes Completion:nil];}
We use - (void)sendEventWithName:(NSString *)eventName body:(id)body
send message, EventName will be the name of the message, body can pass a dictionary as the message body.
So how to accept the message in JS:
First, import Nativeeventemitter, and then we register the Listener event:
Componentwillmount () {//Start listening.varIosexport = Nativemodules.iosexportvarEmitter =NewNativeeventemitter (Iosexport)//Create a listener with the acquired module This. subScription = Emitter.addlistener ("Sendname", (body) = this. _getnotice (body)) //Listen for the specified event, through sendname This event name to identify the event,(body) = = This. _getnotice (body) This is the processing method after hearing the event, body is the message sent by iOS}_getnotice (body) { This. setState ({Notice: body.name+', '+body.age})}
Finally we want to unregister the listener:
componentWillUnmount() { //删除监听 this.subScription.remove()}
The above source has been uploaded gitub, source download
React Native Communication with iOS