Detailed explanation of the interaction between React Native and IOS

Detailed explanation of the interaction between React Native and IOS

Prerequisites

First of all, it is best to understand a little about the grammar of oc

1. Create a declaration file nativeModule.h

#import <Foundation/Foundation.h>
#import <React/RCTBridgeModule.h>

@interface nativeModule : NSObject <RCTBridgeModule>

@end

2. Create the file nativeModule.m

#import <Foundation/Foundation.h>
#import "nativeModule.h"

@interface nativeModule ()

@end

@implementation nativeModule

@end 

This is the directory structure after adding the file.

About the difference of interface:

The @interface in .h is used by other classes to call. Its @property and functions can be "seen" by other classes (public)

The @interface in .m is called Class Extension in OC, which is a supplement to the @interface in the .h file. However, the @interface in the .m file is not open to the outside world and is only visible in the .m file (private)

Therefore, we put the methods and variables that are open to the outside world in the .h file, and put the variables that we do not want to open to the outside world in the .m file (the methods in the .m file can be used directly without declaration).

RN passes value to iOS

Method 1: Pass value to native normally

Add the following method to the .m file:

//Omit the above code @implementation nativeModule

// This code is required to export the module so that the nativeModule module can be accessed 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={'Pass 2 parameters to native'} onPress={() => {
    nativeModule.addHelloWord('your name', 'Location: Zhejiang')
}}/>

Clicking this button will pass the two strings 'your name' and 'location: Zhejiang' to the native end.

Method 2: Passing a callback function

Add in the .m file:

// Accepts only one parameter - an array of parameters to be passed to the JavaScript callback function.
RCT_EXPORT_METHOD(checkIsRoot:(RCTResponseSenderBlock)callback) {
  NSArray *array = @[@"string", @"number"];
  callback(array);
}

Add code in RN:

<Button title={'js passes a callback to native, and receives an array 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 callback after some operations are completed. **If the callback is called multiple times, RN will report an error**

Method 3: Get promise callback

Add the following code to the .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 parameters to RN, callback error information RCT_EXPORT_METHOD(getHBDeviceUniqueID: (RCTPromiseResolveBlock)resolve
                  rejecter:(RCTPromiseRejectBlock)reject) {
  
  // Task to be executed self.normalResolve = resolve;
  self.normalReject = reject;
  
  [self performSelectorOnMainThread:@selector(startTime:) withObject: [NSArray arrayWithObjects: @"1", @"2", nil] waitUntilDone:YES];
}

Add code in RN:

<Button title={'native passes a promise to JS'} onPress={() => {
    nativeModule.getHBDeviceUniqueID().then((arr: string[]) => {
      console.log('resolve', arr)
    }).catch((err: string) => {
      console.error(err)
    })
}}/>

The execution of nativeModule.getHBDeviceUniqueID is a promise that can get the callback of the native end, which is actually similar to method 2.

Method 4: Synchronous way to get promise

Add in the .m 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 is the same as RCT_EXPORT_METHOD, but this method is called synchronously from JS on the JS thread and may return a result.
// Synchronization may have performance issues. It is recommended not to use 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 on the RN side:

<Button title={'native passes a promise to JS2'} onPress={() => {
    nativeModule.findEvents().then((arr: string[]) => {
      console.log('resolve', arr)
    }).catch((err: string) => {
      console.error(err)
    })
}}/>

Method 4 is basically the same as method 3, but there is one difference, that is, RCT_REMAP_METHOD uses this method to put the code into a synchronous state.

iOS passes value to RN end

Initial data provision

Add the following code to appDelegate.m:

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 already exists, the difference is initialProperties:props

Write on the RN side:

// Rewrite APP, images is the data provided by iOS, here we pass data through context export default class App extends React.Component<{ images: string[] }> {

  render() {
    return <NativeProps.Provider value={this.props.images}>
      <AppContainer/>
    </NativeProps.Provider>
  }
}

// Simply use in hooks const images = useContext(NativeProps);

<Text>This is the initial data from the native side {JSON.stringify(images)}</Text>

Adding event listeners

Add the following code to the .m file:

// Event names available for monitoring - (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 from 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 in RN:

const ManagerEmitter = new NativeEventEmitter(nativeModule)

const [msg, setMsg] = useState([])

// Use in hooks, similar to componentDidMount lifecycle useEffect(() => {
    const subscription = ManagerEmitter.addListener(
      'EventReminder',
      (reminder) => {
        setMsg(prevState => {
          return prevState.concat(reminder.name)
        })
        console.log('This is the monitored EventReminder event response', reminder.name)
      }
    )

    return () => {
      subscription.remove()
    }
}, [])


<Button title={'js listens to events, let native send notifications to js'} onPress={() => {
    nativeModule.postNotificationEvent('test')
}}/>

<Button title={'js listens to events, let native send notifications to js'} onPress={() => {
    nativeModule.Send()
}}/>

{
    msg.map((item, index) => {
      return <Text key={item + index}>item:{item}</Text>
    })
}

The postNotificationEvent method is the simplest to use. Calling sendEventWithName on the native side can pass data to the RN listener.

The other method is Send and calendarEventReminderReceived. One is from the official website. The example is about getting data from NSNotification, and Send is passing data to calendarEventReminderReceived.

Regarding the optimization of monitoring, there is also a link on the official website. You can take a look at it when you have time. Just add the following code to the .m file:

@implementation nativeModule
{
  bool hasListeners;
  // a local variable }

-(void)startObserving {
  hasListeners = YES;
}

-(void)stopObserving {
  hasListeners = NO;
}
// Add judgment when sending listeners. Send only if there are listeners, effectively reducing the calls of bridge code if (hasListeners) { 
    [self sendEventWithName:@"EventReminder" body:@{@"name": name}];;
}

Summarize

The repository for the above code: https://github.com/Grewer/learn-rn

That's basically it about the interaction between the native side and the RN side. Of course, there are more and more complex operations on the native side, such as processes. If you want to write a bridge method, you will encounter a lot of this. However, mastering the above is enough for calling some third-party SDKs.

The above is a detailed explanation of the interaction between React Native and IOS. For more information about the interaction between React Native and IOS, please pay attention to other related articles on 123WORDPRESS.COM!

You may also be interested in:
  • React axios cross-domain access to one or more domain names
  • Reactnative-iOS callback Javascript method
  • How to implement differential incremental updates on React Native on iOS
  • How to combine NavigatorIOS and ListView in react-native components
  • Sample code for various interactions between iOS native and react-native
  • React Native third-party platform sharing examples (Android, IOS dual platform)
  • Solution to the problem that IOS React and other Titles do not display
  • React-Native Android and IOS App use one code to implement
  • IOS React Native FlexBox detailed explanation and examples

<<:  Detailed explanation of common commands in Docker repository

>>:  Summary of the difference between using from and join to query two tables in MySQL

Recommend

Element-ui's built-in two remote search (fuzzy query) usage explanation

Problem Description There is a type of query call...

IIS7 IIS8 reverse proxy rule writing, installation and configuration method

Purpose: Treat Station A as the secondary directo...

Alpine Docker image font problem solving operations

1. Run fonts, open the font folder, and find the ...

Two ways to reset the root password of MySQL database using lnmp

The first method: Use Junge's one-click scrip...

Example code of how CSS matches multiple classes

CSS matches multiple classes The following HTML t...

CSS achieves colorful and smart shadow effects

background Ever wondered how to create a shadow e...

Getting Started with Nginx Reverse Proxy

Table of contents Overview The role of reverse pr...

Sample code for implementing 3D book effect with CSS

Without further ado, let's take a look at the...

Detailed explanation of the functions of each port of Tomcat

From the tomcat configuration file, we can see th...

Install centos7 virtual machine on win10

1. Download VMware Workstation 64 version https:/...

Detailed explanation of two points to note in vue3: setup

Table of contents In vue2 In vue3 Notes on setup ...

HTML Tutorial: HTML horizontal line segment

<br />This tag can display a horizontal line...

jQuery implements Table paging effect

This article shares the specific code of jQuery t...

Detailed process of Vue front-end packaging

Table of contents 1. Add packaging command 2. Run...