React Native iOS Mixed Development

Links to the original text: https://www.jianshu.com/p/66eb84f13bd8

When developing RN, communication between JS and Native is usually indispensable. For example, when initializing RN, Native transfers data to JS, JS calls Native's album selection picture, JS calls Native's module for some complicated calculation, Native actively transfers some data (GPS information, gyroscope, sensor, etc.) to JS.

In this article, I will introduce several ways of communication between JS and Native in RN, as well as their principles and techniques.

Next, I will introduce the communication between JS and Native in several scenarios.

Several communication scenarios:

  • Native transfers data to JS when RN is initialized.
  • Native sends data to JS;
  • JS sends data to Native;
  • JS sends data to Native, and Native returns data to JS.

React-Native-JS-Native-Communication

1. Native transfers data to JS when RN is initialized

init-data-to-js

The API of RN provides Native's way of transferring data to JS when initializing JS pages. This way of transferring data occurs earlier than the other ways of transferring data described below.

Because there is very little information about this method, so there may be many friends do not know this method, but it does not matter, then I will show you how to use this method to transfer data to JS.

concept

RN allows us to pass props data to top-level JS components when initializing JS pages, which can be retrieved by top-level components through this.props.

iOS

[[RCTRootView alloc] initWithBundleURL: jsCodeLocation
                                moduleName: self.moduleName //This "App1" name must be consistent with the name we registered in index.js
                         initialProperties:@{@"params":self.paramsInit}//Initialization data passed to JS during RN initialization
                             launchOptions: nil];

Next, let's look at how to pass these initialization data on iOS.

iOS Passes Initial Properties to RN

RN's RCTRootView provides an initWithBundle URL method to render a JS component, in which a parameter is provided to initialize the data passed to the JS component.

Method prototype:

- (instancetype)initWithBundleURL:(NSURL *)bundleURL moduleName:(NSString *)moduleName
        initialProperties:(NSDictionary *)initialProperties launchOptions:(NSDictionary *)launchOptions
  • jsCodeLocation: The path of the JS page of the RN to be rendered;
  • moduleName: The name of the JS module to be loaded;
  • Initial Properties: Initial data to be passed to top-level JS components;
  • Launch Options: Mainly used when AppDelegate loads JS Bundle, just pass nil here.

With the third parameter of the above method, a data of NSDictionary type can be passed to the top-level JS component.

Sample code:

[[RCTRootView alloc] initWithBundleURL: jsCodeLocation
                                moduleName: self.moduleName
                         initialProperties:@{@"params":@"This is passed on to the top level. JS Component data"}//Initialization data passed to JS during RN initialization
                             launchOptions: nil];

In the above code, we pass a data named params, which is passed to the top-level JS component, to the top-level JS component, and then we can get this data in the top-level JS component by the following methods:

 render() {
        const {params}=this.props;
        return (
            <View style={styles.container}>
                <Text style={styles.data}>Come from Native Initialize data:{params}</Text>
            </View>
            );
 }

In addition, if you want to use this initialization data in non-top-level pages such as CommonPage, you can pass the data to the CommonPage page as follows:

export default class App extends Component<Props> {
    ...
    render() {
        return <CommonPage  {...this.props}/>;
    }
    ...
}

2. Communication between Native and JS (Native sends data to JS)

init-data-to-js

An RCTEventEmitter interface is provided in the iOS SDK of RN, through which we can realize the communication between Native and JS, that is, Native transfers data to JS.

Method prototype:

- (void)sendEventWithName:(NSString *)name body:(id)body;

So as long as we get an example of RCTEventEmitter, we can use it to pass data to JS. In order to obtain an example of RCTEventEmitter, we can inherit RCTEventEmitter < RCTBridgeModule > to achieve:

DataToJSPresenter.h

/**
 * React Native JS Native Signal communication
 * Author: CrazyCodeBoy
 * Video tutorial: https://coding.imooc.com/lesson/89.html#mid=2702
 * GitHub:https://github.com/crazycodeboy
 * Email:crazycodeboy@gmail.com
 */
#import <React/RCTBridgeModule.h>
#import <React/RCTEventEmitter.h>

@interface DataToJSPresenter : RCTEventEmitter <RCTBridgeModule>

@end

DataToJSPresenter.m

/**
 * React Native JS Native Signal communication
 * Author: CrazyCodeBoy
 * Video tutorial: https://coding.imooc.com/lesson/89.html#mid=2702
 * GitHub:https://github.com/crazycodeboy
 * Email:crazycodeboy@gmail.com
 */
#import "DataToJSPresenter.h"

@implementation DataToJSPresenter

RCT_EXPORT_MODULE();

- (NSArray<NSString *> *)supportedEvents
{
    return @[@"testData"];
}
- (instancetype)init {
    if (self = [super init]) {//Register fireData broadcast when module is initialized
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(fireData:) name:@"fireData" object:nil];
    }
    return self;
}
- (void)fireData:(NSNotification *)notification{//Send data to RN
    NSString *eventName = notification.object[@"name"];
    NSDictionary *params = notification.object[@"params"];
    [self sendEventWithName:eventName body:params];
}

@end

In the above method, we pass the data params named eventName to JS through the sendEventWithName method of RCTEventEmitter.

Tip: In DataToJSPresenter, we implemented the (NSArray < NSString *>*) supported Events method, which is used to specify the event name that can be sent to JS, so event Name sent to JS must be configured in this method or it cannot be sent.

The steps needed to implement communication from Native to JS

Next, let's summarize the steps needed to achieve the communication between Native and JS:

  • Firstly, RCTEventEmitter < RCTBridgeModule > should be implemented.
  • The data is passed to JS through the sendEventWithName method of RCTEventEmitter.

Through the above steps, we can start data from Native to JS, so how to get the data in JS?

Get data from Native through RCTEventEmitter in JS

In JS, Native Event Emitter can be used to obtain data from Native through RCTEventEmitter. The specific methods are as follows:

import {NativeEventEmitter} from 'react-native';
export default class CommonPage extends Component<Props> {
    constructor(props) {
        super(props);
        this.state = {
            data: "",
            result: null
        }
    }

    componentWillMount() {
        this.dataToJSPresenter = new NativeEventEmitter(NativeModules.DataToJSPresenter);
        this.dataToJSPresenter.addListener('testData', (e) => {// for iOS
            this.setState({
                data: e.data
            })
        })
    }

    componentWillUnmount() {
        if (this.dataToJSPresenter){
            this.dataToJSPresenter.removeListener('testData');
        }
    }

    render() {
        return (
            <View style={styles.container}>
                <Text style={styles.data}>Received Native Data:{this.state.data}</Text>
            </View>
        );
    }
}

In the above code, we added a listener through the addListener of Native Event Emitter, which listens for data sent by Native called testData, which is consistent with the event Name mentioned above:

[self sendEventWithName:eventName body:params];

https://coding.imooc.com/lesson/89.html#mid=2702
Also, remember to remove the listener in time when the JS component is uninstalled.

Above is the principle and method of realizing the communication between JS and Native in iOS. Next, let's look at the principle and method of realizing the communication between JS and Native.

3. JS-to-Native communication (JS sends data to Native)

init-data-to-js

What we encapsulate NativeModule It is for JS. It is a bridge between JS and Native. JS can communicate with Native through it (transfer data, open Native pages, etc.). Then I will use it. NativeModule Realize the communication between JS and Native.

You can learn how to implement NativeModule Encapsulation of React Native Prototype

First implement JSBridgeModule

First we need to implement RCTBridgeModule:

JSBridgeModule.h

/**
 * React Native JS Native Signal communication
 * Author: CrazyCodeBoy
 * Video tutorial: https://coding.imooc.com/lesson/89.html#mid=2702
 * GitHub:https://github.com/crazycodeboy
 * Email:crazycodeboy@gmail.com
 */
#import <React/RCTBridgeModule.h>
@interface JSBridgeModule : NSObject <RCTBridgeModule>

@end

JSBridgeModule.m

/**
 * React Native JS Native Signal communication
 * Author: CrazyCodeBoy
 * Video tutorial: https://coding.imooc.com/lesson/89.html#mid=2702
 * GitHub:https://github.com/crazycodeboy
 * Email:crazycodeboy@gmail.com
 */
#import "JSBridgeModule.h"

@implementation JSBridgeModule

RCT_EXPORT_MODULE();
- (dispatch_queue_t)methodQueue
{
    return dispatch_get_main_queue();//Let RN call back these methods in the main thread
}
RCT_EXPORT_METHOD(sendMessage:(NSDictionary*)params){//Accept messages from RN
    [[NSNotificationCenter defaultCenter] postNotificationName:@"sendMessage" object:params];
}
@end

Code parsing

  1. In JSBridgeModule, we implement a RCT_EXPORT_METHOD (sendMessage:(NSDictionary*) params method, which is mainly used to expose JS calls to pass data params to Native;
  2. When the data is received, the data is sent out in the form of notification through the NSNotification Center.

JS calls JSBridgeModule to send data to Native

import {NativeModules} from 'react-native';

const JSBridge = NativeModules.JSBridgeModule;

JSBridge.sendMessage({text: this.text})

With the above code, I can pass a Map-type data {text: this.text} to Native.

4. JS sends data to Native, and Native returns data to JS

init-data-to-js

Through the JS-to-Native communication (JS sending data to Native) mentioned above, we have realized the JS-to-Native communication. At that time, we relied on JSBridge Module. In fact, its function is not limited to this. With the help of JS, we can also realize the data return from Native to JS.

Implementation in Native

Add the following method to JSBridgeModule:

RCT_EXPORT_METHOD(doAdd:(NSInteger )num1 num2:(NSInteger )num2 resolver:(RCTPromiseResolveBlock)resolve
                  rejecter:(RCTPromiseRejectBlock)reject)
{
    NSInteger result=num1+num2;
    resolve([NSString stringWithFormat:@"%ld",(long)result]);//Callback JS
}

The above code exposes JS to a simple addition operation between two integers and passes the result back to JS, where we use two types of callbacks, RCTPromiseResolveBlock and RCTPromiseRejectBlock, representing success and failure, respectively.

Implementation in JS

import {NativeModules} from 'react-native';

const JSBridge = NativeModules.JSBridgeModule;
JSBridge.doAdd(parseInt(this.num1), parseInt(this.num2)).then(e => {
    this.setState({
        result: e
    })
})

In JS, we pass two integers num1 and num2 to Native through JSBridge.doAdd method, and then listen for the result through the then. The whole process adopts Promise's chain call mode.

 

Tags: React github iOS SDK

Posted on Wed, 14 Aug 2019 04:06:05 -0700 by smti