一、从React Native中调用原生方法(原生模块)

  原生模块是JS中也可以使用的Objective-C类。一般来说这样的每一个模块的实例都是在每一次通过JS bridge通信时创建的。他们可以导出任意的函数和常量给React Native。相关细节可以参阅这篇文章

  在React Native中,一个“原生模块”就是一个实现了“RCTBridgeModule”协议的Objective-C类,其中RCT是ReaCT的缩写。



// CalendarManager.h
#import <React/RCTBridgeModule.h>
#import <React/RCTLog.h>

@interface CalendarManager : NSObject <RCTBridgeModule>
@end



  为了实现RCTBridgeModule协议,你的类需要包含RCT_EXPORT_MODULE()宏。这个宏也可以添加一个参数用来指定在Javascript中访问这个模块的名字。如果你不指定,默认就会使用这个Objective-C类的名字。



// CalendarManager.m
@implementation CalendarManager

RCT_EXPORT_MODULE();

@end


//支付宝支付
RCT_EXPORT_METHOD(onAliPay:(NSString *)orderString  resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
  //模块代码  
}



  现在从Javascript里可以这样调用这个方法:



import {
    NativeModules
} from 'react-native';
var pay = NativeModules.ReactNativePay;

 Pay.onAliPay(payStr) .then((message)=>{
      console.log("message" + message); if(message !== "")
        //支付成功的处理
      this.refs.toast.show(message, DURATION.LENGTH_SHORT);
    }) .catch(e=>{
      console.log("e.message" + e.message);
      if(e.message !== "") this.refs.toast.show(e.message, DURATION.LENGTH_SHORT);
      if(e.code === '-1' || e.message === '支付失败') {
        //支付失败
      }
    })



  注意: 导出到Javascript的方法名是Objective-C的方法名的第一个部分。React Native还定义了一个RCT_REMAP_METHOD()宏,它可以指定Javascript方法名。当许多方法的第一部分相同的时候用它来避免在Javascript端的名字冲突。

二、参数类型

  RCT_EXPORT_METHOD 支持所有标准JSON类型,包括:

  • string (NSString)
  • number (NSIntegerfloatdoubleCGFloatNSNumber)
  • boolean (BOOLNSNumber)
  • array (NSArray) 包含本列表中任意类型
  • object (NSDictionary) 包含string类型的键和本列表中任意类型的值
  • function (RCTResponseSenderBlock)

  除此以外,任何RCTConvert类支持的的类型也都可以使用(参见RCTConvert了解更多信息)。RCTConvert还提供了一系列辅助函数,用来接收一个JSON值并转换到原生Objective-C类型或类。如下:



RCT_EXPORT_METHOD(addEvent:(NSString *)name location:(NSString *)location date:(nonnull NSNumber *)secondsSinceUnixEpoch)
{
  NSDate *date = [RCTConvert NSDate:secondsSinceUnixEpoch];
}

或

RCT_EXPORT_METHOD(addEvent:(NSString *)name location:(NSString *)location date:(NSString *)ISO8601DateString)
{
  NSDate *date = [RCTConvert NSDate:ISO8601DateString];
}



三、回调函数

  原生模块还支持一种特殊的参数——回调函数。它提供了一个函数来把返回值传回给JavaScript。如下:



typedef void (^RCTResponseSenderBlock)(NSArray *response);

typedef void (^RCTResponseErrorBlock)(NSError *error);

typedef void (^RCTPromiseResolveBlock)(id result);

typedef void (^RCTPromiseRejectBlock)(NSString *code, NSString *message, NSError *error);



 四、实例代码(一)

  (1)原生代码



//.h文件
#import <React/RCTBridgeModule.h>
#import <React/RCTLog.h>


@interface RNMethodTool : NSObject <RCTBridgeModule>
@end



//.m文件
#import "RNMethodTool.h"
#import "AppDelegate.h"
@implementation RNMethodTool
//这个宏必须要写的,否则RN找不到该类
RCT_EXPORT_MODULE()

RCT_EXPORT_METHOD(doSomething:(NSString *)string){
  //这也是暴露给RN的方法,弹出系统框,stirng是RN传过来的参数
  dispatch_async(dispatch_get_main_queue(), ^{
    UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"我是iOS系统框  RN 调用 原生方法"  message:string preferredStyle:UIAlertControllerStyleAlert];
    
    UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:nil];
    UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"好的" style:UIAlertActionStyleDefault handler:nil];
    
    [alertController addAction:cancelAction];
    [alertController addAction:okAction];
    
    AppDelegate *delegate = (AppDelegate *)([UIApplication sharedApplication].delegate);
    [delegate.window.rootViewController presentViewController:alertController animated:YES completion:nil];
  });
}

@end



  (2)在JS端的应用



import React, {Component} from 'react';
import {Platform, StyleSheet, Text, View,NativeModules,TouchableHighlight} from 'react-native';
const methodTool = NativeModules.RNMethodTool;

type Props = {};
export default class App extends Component<Props> {
  render() {
    return (
      <View style={styles.container}>
        <TouchableHighlight onPress={()=>methodTool.doSomething("我是rn传过来的数据")}>
          <Text style={styles.welcome}>Welcome to React Native!</Text>
        </TouchableHighlight>
      </View>
    );
  }
  
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#F5FCFF',
  },
  welcome: {
    fontSize: 20,
    textAlign: 'center',
    margin: 10,
  },
});



 五、实例代码(二)

  原生端



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

@interface NativeInteraction : NSObject <RCTBridgeModule>

@end


//.m实现文件
#import "NativeInteraction.h"

#define TestNativeJsonData @"{\"callback1\":\"123\",\"callback2\":\"asd\"}"

@implementation NativeInteraction
{
  RCTPromiseResolveBlock _resolveBlock;
  RCTPromiseRejectBlock _rejectBlock;
}
RCT_EXPORT_MODULE();

RCT_EXPORT_METHOD(RNTransferIOS) {
  NSLog(@"RN调用iOS");
}

RCT_EXPORT_METHOD(RNTransferIOSWithParameter:(NSString *)logString) {
  NSLog(@"来自RN的数据:%@",logString);
}

RCT_EXPORT_METHOD(RNTransferIOSWithCallBack:(RCTResponseSenderBlock)callback) {
  callback(@[[NSString stringWithFormat:@"来自iOS Native的数据:%@",TestNativeJsonData]]);
}

RCT_REMAP_METHOD(RNTransferIOSWithParameterAndCallBack, para1:(NSString *)para1 para2:(NSString *)para2 resolver:(RCTPromiseResolveBlock)resolver rejecter:(RCTPromiseRejectBlock)reject) {
  NSLog(@"来自RN的数据:para1——%@, para2——%@",para1, para2);

  _resolveBlock=resolver;
  _rejectBlock=reject;
  NSString *jsonString = TestNativeJsonData;
  _resolveBlock(@[jsonString]);
}


@end



   在JS端的应用



import React, {Component} from 'react';
import {Platform, StyleSheet, Text, View, DeviceEventEmitter, Alert, Button, NativeModules} from 'react-native';

const NativeInteraction = NativeModules.NativeInteraction;

type Props = {};
export default class App extends Component<Props> {

  constructor(props) {
    super(props);
    this.state = {
        notice: '默认值',
    };
  }

  componentWillMount() {
      DeviceEventEmitter.addListener('EventInit', (msg) => {
          let receive = "EventInit: " + msg;
          console.log(receive);
          this.setState({notice: receive});
      });

      DeviceEventEmitter.addListener('EventLogin', (msg) => {
          let receive = "EventLogin: " + msg;
          console.log(receive);
          this.setState({notice: receive});
      });
  }

  transferIOS = () => {
    NativeInteraction.RNTransferIOS();
  }

  transferIOS1 = () => {
    NativeInteraction.RNTransferIOSWithParameter('{\'para1\':\'rndata1\',\'para2\':\'rndata2\'}');
  }

  transferIOS2 = () => {
    NativeInteraction.RNTransferIOSWithCallBack((data) => {
      this.setState({notice: data});
    });
  }

  transferIOS3 = () => {
    NativeInteraction.RNTransferIOSWithParameterAndCallBack('rndata1','rndata2').then(result =>{
      let jsonString = JSON.stringify(result);
      this.setState({notice: jsonString});
    }).catch(error=>{
    });
  }

  render() {
    return (
      <View style={styles.container}>
        <Text style={styles.welcome}>第一个React Native页面</Text>
        <Text style={styles.instructions}>{this.state.notice}</Text>
        <Button
          onPress={this.transferIOS}
          title="RN调用iOS(无回调无参数)"
          color="#841584"
          />
        <Button
          onPress={this.transferIOS1}
          title="RN调用iOS(有参数)"
          color="#841584"
          />
        <Button
          onPress={this.transferIOS2}
          title="RN调用iOS(有回调)"
          color="#841584"
          />
        <Button
          onPress={this.transferIOS3}
          title="RN调用iOS(有参数有回调)"
          color="#841584"
          />
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#F5FCFF',
  },
  welcome: {
    fontSize: 20,
    textAlign: 'center',
    margin: 10,
  },
  instructions: {
    textAlign: 'center',
    color: '#333333',
    marginBottom: 5,
  },
});