Pre preparation
First of all, it's better to know a little about oc grammar, otherwise many of them can't be understood
-
Create declaration file nativeModule.h
#import <Foundation/Foundation.h> #import <React/RCTBridgeModule.h> @interface nativeModule : NSObject <RCTBridgeModule> @end
-
Create the file nativeModule.m
#import <Foundation/Foundation.h> #import "nativeModule.h" @interface nativeModule () @end @implementation nativeModule @end
This is the structure directory after adding files
About the difference of interface:
The @ interface in. h, which is called by other classes. Its @ property and functions can be "seen" (public) by other classes
The @ interface in. m, called Class Extension in OC, is the supplement of @ interface in. h file. However, the @ interface in the. m file is not open to the outside world, only visible in the. m file (private)
Therefore, we put the methods and variables that are open to the outside world into the. h file, and put the variables that are not open to the outside world into the. M file (. The methods of the m file can be used directly without declaration).
RN to iOS
Method 1 normal transfer value to native
Add method in. m file:
// Omit the above code @implementation nativeModule // This code is required to export the module, so as to access the nativeModule module in RN RCT_EXPORT_MODULE(); // Receive string RCT_EXPORT_METHOD(addHelloWord:(NSString *)name location:(NSString *)location) { NSLog(@"%@,%@", name, location); } @end
RN Code:
import { Button, NativeModules } from 'react-native' const { nativeModule } = NativeModules <Button title={'Send 2 parameters to native'} onPress={() => { nativeModule.addHelloWord('Your Name', 'position:Zhejiang') }}/>
The function of clicking this button is to pass the two strings' your name 'and' location: Zhejiang 'to the native end
Method 2 pass the callback function
Add in. m file:
// Only one parameter is accepted -- an array of parameters passed to the JavaScript callback function. RCT_EXPORT_METHOD(checkIsRoot:(RCTResponseSenderBlock)callback) { NSArray *array = @[@"string", @"number"]; callback(array); }
Add code to RN:
<Button title={'js Send a callback to native,An array was received in the callback'} onPress={() => { nativeModule.checkIsRoot((str: string, num: string) => { console.log(str, num) }) }}/>
This is a callback function passed to the native end in RN to solve the problem. If the callback calls RN multiple times, an error will be reported
Method 3 get promise callback
Add code to. m file:
@interface nativeModule () @property (nonatomic) RCTPromiseResolveBlock normalResolve; @property (nonatomic) RCTPromiseRejectBlock normalReject; @property (nonatomic) NSInteger num; @end // This is a timer -(void)startTime: (NSArray*) data{ NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:2 repeats:YES block:^(NSTimer * _Nonnull timer) { NSArray *events =@[@"Promise ",@"test ",@" array"]; if (events) { self.normalResolve(events); [timer invalidate]; } else { [timer invalidate]; NSError *error=[NSError errorWithDomain:@"I am calling back the error message..." code:101 userInfo:nil]; self.normalReject(@"no_events", @"There were no events", error); } }]; [[NSRunLoop mainRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode]; } // Callback parameter to RN, error message of callback RCT_EXPORT_METHOD(getHBDeviceUniqueID: (RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) { // Tasks to perform self.normalResolve = resolve; self.normalReject = reject; [self performSelectorOnMainThread:@selector(startTime:) withObject: [NSArray arrayWithObjects: @"1", @"2", nil] waitUntilDone:YES]; }
Add code to RN:
<Button title={'native Pass on one promise to JS'} onPress={() => { nativeModule.getHBDeviceUniqueID().then((arr: string[]) => { console.log('resolve', arr) }).catch((err: string) => { console.error(err) }) }}/>
nativeModule.getHBDeviceUniqueID
He's a promise,Can get the callback of the native end, It's similar to method 2Method 3 obtaining promise Synchronization mode of
stay .m Add to file:
// This is a timer 2 -(void)startTime2: (NSArray*) data{ NSLog(@"data%@",data); NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1 repeats:YES block:^(NSTimer * _Nonnull timer) { NSLog(@"%d", (int)self.num); self.num = self.num + 1; NSLog(@"%d", (int)self.num); if (self.num > 4) { [timer invalidate]; NSLog(@"end"); self.normalResolve(data); } }]; [[NSRunLoop mainRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode]; } // RCT_REMAP_METHOD and RCT_ EXPORT_ The method is the same, but the method is called synchronously from JS on the JS thread and may return results. // Synchronization may have performance problems. It is not recommended to use any other than promise RCT_REMAP_METHOD(findEvents, findEventsWithResolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) { self.normalResolve = resolve; self.normalReject = reject; self.num = 0; [self performSelectorOnMainThread:@selector(startTime2:) withObject: [NSArray arrayWithObjects: @"1", @"2", nil] waitUntilDone:YES]; }
Add code at RN end:
<Button title={'native Pass on one promise to JS2'} onPress={() => { nativeModule.findEvents().then((arr: string[]) => { console.log('resolve', arr) }).catch((err: string) => { console.error(err) }) }}/>
Method 4 and method 3 are basically the same, but there is a difference, namely RCT_REMAP_METHOD use this method to change the code to synchronous state
iOS sends value to RN
Initial data provision
Add code in appDelegate.m file:
NSArray *imageList = @[@"http://foo.com/bar1.png", @"http://foo.com/bar2.png"]; NSDictionary *props = @{@"images" : imageList}; RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge moduleName:@"learn" initialProperties:props]; // This line of code is original, the difference is initialp roperties:props
Write in RN:
// Rewrite app. Images is the data provided by iOS. Here we pass the data through context export default class App extends React.Component<{ images: string[] }> { render() { return <NativeProps.Provider value={this.props.images}> <AppContainer/> </NativeProps.Provider> } } // Simple use in hooks const images = useContext(NativeProps); <Text>This is from native Initial data from the end{JSON.stringify(images)}</Text>
Add listening event
Add code to. m file:
// Event name available for listening - (NSArray<NSString *> *)supportedEvents { return @[@"EventReminder"]; } RCT_EXPORT_METHOD(postNotificationEvent:(NSString *)name) { NSLog(@"calendarEventReminderReceived"); [self sendEventWithName:@"EventReminder" body:@{@"name": name}];; } - (void)calendarEventReminderReceived:(NSNotification *)notification { // This is an example of the official website NSLog(@"calendarEventReminderReceived"); NSString *eventName = notification.userInfo[@"name"]; [self sendEventWithName:@"EventReminder" body:@{@"name": eventName}]; } RCT_EXPORT_METHOD(Send){ NSDictionary *dict = @{@"name" : @"veuimyzi"}; NSNotification *notification = [[NSNotification alloc] initWithName:@"EventReminder" object:nil userInfo:dict] ; [self calendarEventReminderReceived:notification]; }
Add code to RN:
const ManagerEmitter = new NativeEventEmitter(nativeModule) const [msg, setMsg] = useState([]) // Use in hooks, similar to the component didmount lifecycle useEffect(() => { const subscription = ManagerEmitter.addListener( 'EventReminder', (reminder) => { setMsg(prevState => { return prevState.concat(reminder.name) }) console.log('This is monitored EventReminder Event response', reminder.name) } ) return () => { subscription.remove() } }, []) <Button title={'js Listening events,Give Way native to js Give notice'} onPress={() => { nativeModule.postNotificationEvent('test') }}/> <Button title={'js Listening events,Give Way native to js Give notice send'} onPress={() => { nativeModule.Send() }}/> { msg.map((item, index) => { return <Text key={item + index}>item:{item}</Text> }) }
The postNotificationEvent method is the simplest use. When sendEventWithName is called on the native side, data can be passed to RN for listening
Another method, Send and calendareventremindereceived, is an example from the official website, which is to get data from NSNotification, and Send is to pass data to calendareventremindereceived
As for the optimization of monitoring, it is also available on the official website. If you have time, you can see the following code in the. m file:
@implementation nativeModule { bool hasListeners; // A local variable } -(void)startObserving { hasListeners = YES; } -(void)stopObserving { hasListeners = NO; } // Add judgment in sending listening, send only when there is listening, effectively reducing the call of bridge code if (hasListeners) { [self sendEventWithName:@"EventReminder" body:@{@"name": name}];; }
summary
Library of the above code: https://github.com/Grewer/lea...
This is basically the interaction between the native end and the RN end. Of course, there are more and more complex operations on the native end, such as processes. If you want to write bridging methods, you will encounter a lot of them. However, if you master the above, it is enough for some three-party SDK calls