美文网首页iOS Developer程序员程序猿阵线联盟-汇总各类技术干货
React Native 与原生模块数据通信(一)(iOS)

React Native 与原生模块数据通信(一)(iOS)

作者: manofit | 来源:发表于2018-05-24 14:50 被阅读44次

    (一)iOS日历模块封装演示

    下面开始演示如何封装一个iOS日历原生模块,让JavaScript可以进行访问到iOS平台日历的功能。

    在React Native中,原生模块就是一个Objective-C类,并且应该实现了RCTBridgeModule协议。其中RCT是ReaCT的缩写。我们看一下CalendarManger.h官方的代码:

    // CalendarManager.h
    #import "RCTBridgeModule.h"
    @interface CalendarManager : NSObject <RCTBridgeModule>
    
    @end
    

    为了实现RCTBridgeModule协议,你的CalendarManger类.m文件需要实现RCT_EXPORT_MODULE()宏,该宏有一个可选的参数可以设置指定在JavaScript端进行访问该模块的名称(更多细节可以往后面继续看)。如果你这边没有设置该名称,那么JavaScript会默认去使用Objective-C类名称。

    到这一步CalendarManager.m文件的配置如下:

    #import "CalendarManager.h"
    
    @implementation CalendarManager
    RCT_EXPORT_MODULE()
    @end
    

    下面就是注入提供给JavaScript调用的方法,该方法必须要使用RCT_EXPORT_METHOD()宏来声明,不然这些方法JavaScript前端是无法进行调用的。看下面官方的代码:

    RCT_EXPORT_METHOD(addEvent:(NSString *)name location:(NSString *)location)
    {
        NSLog(@"Pretending to create an event %@ at %@", name, location);
    }
    

    完成以上的步骤,我们就可以从JavaScript端进行访问这个方法了。具体调用方式如下:

    import { NativeModules } from 'react-native';
    var CalendarManager = NativeModules.CalendarManager;
    CalendarManager.addEvent('Birthday Party', '4 Privet Drive, Surrey');
    

    来最终JavaScript中的代码如下:

    /**
     * Sample React Native App
     * https://github.com/facebook/react-native
     */
    import React,{
      AppRegistry,
      Component,
      StyleSheet,
      Text,
      View,
      TouchableHighlight,
    } from 'react-native';
    ///进行导入NativeModules中的CalendarManger模块
    import { NativeModules } from 'react-native';
    var CalendarManager = NativeModules.CalendarManager;
    class CustomButton extends React.Component {
      render() {
        return (
          <TouchableHighlight
            style={styles.button}
            underlayColor="#a5a5a5"
            onPress={this.props.onPress}>
            <Text style={styles.buttonText}>{this.props.text}</Text>
          </TouchableHighlight>
        );
      }
    }
    class ModulesDemo extends Component {
      render() {
        return (
          <View style={{marginTop:20}}>
            <Text style={styles.welcome}>
                封装iOS原生模块实例
            </Text>
            <CustomButton text="点击调用原生模块addEvent方法"
                onPress={()=>CalendarManager.addEvent('生日聚会', '江苏南通 中天路')}
            />
          </View>
        );
      }
    }
    const styles = StyleSheet.create({
      welcome: {
        fontSize: 20,
        textAlign: 'center',
        margin: 10,
      },
      button: {
        margin:5,
        backgroundColor: 'white',
        padding: 10,
        borderWidth:1,
        borderColor: '#cdcdcd',
      },
    });
    AppRegistry.registerComponent('ModulesDemo', () => ModulesDemo);
    

    点击按钮,会去调用原生模块方法,并且传入数据打印在控制台,效果截图如下:


    22.jpg

    [特别注意].JavaScript调用方法名称

    封装的原生模块方法给JavaScript进行调用,通过该方法的名字进行调用。有时候可能出现同名冲突的问题,在React Native中,还好给我们定义了RCT_REMAP_METHOD()宏来进行重置JavaScript调用的方法名称。该宏在多个原生模块中可能封装了同样的方法名称非常有用,可以解决同名冲突问题。

    CalendarManager模块在Objective-C中必须要使用[CalendarManager new]进行初始化,并且该用于桥接调用的方法的返回值始终为void。React Native桥接的方法是异步的,所以传递一个返回值给JavaScript只能通过回调或者发送事件解决(具体我们会在后面的文章中讲解)。

    (二)参数类型

    RCT_EXPORT_METHOD支持所有的标准的JSON对象类型,如下:

    • string(NSString )

    • number(NSInteger,float,double,CGFloat,NSNumber)

    • boolean(BOOL,NSNumber)

    • array(NSArray) 任何类型的集合

    • map(NSDictionary) 字典类型,包括任何类型键值对的集合

    • function(RCTResponseSenderBlock) block回调对象方法

    除此之外,支持RCTConvert类支持的类型也都支持(大家可以查看RCTConvert来了解详情)。RCTConvert还提供了一些方法可以把输入接收的JSON数据转换成原生的Objective-C类型或者类。

    例如在我们本个CalendarManger例子中,我们需要通过桥接把JavaScript中的事件的时间发送给原生封装的方法,但是我是不能直接通过桥接进行传递JavaScript的时间对象的,所以我们这边需要把时间转换成一个字符串或者数字进行传递。下面我们来看一下实例的方法:

    //对外提供调用方法,为了演示事件时间格式化
    RCT_EXPORT_METHOD(addEventMore:(NSString *)name location:(NSString *)location data:(NSNumber *)secondsSinceUnixEpoch){
       NSDate *date = [RCTConvert NSDate:secondsSinceUnixEpoch];
    }
    

    或者像以下的写法:

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

    其实我们还可以依靠类型自动转换的原理,跳过手动类型转换的步骤,直接像如下这样写,也是可以的

    RCT_EXPORT_METHOD(addEvent:(NSString *)name location:(NSString *)location date:(NSDate *)date)
    {
      // Date is ready to use!
    }
    

    接下来我们就可以在JavaScript文件中使用如下两种方法的一种进行调用了

    CalendarManager.addEvent('Birthday Party', '4 Privet Drive, Surrey', date.getTime()); //把日期以unix时间戳形式传递
    

    或者:

    CalendarManager.addEvent('Birthday Party', '4 Privet Drive, Surrey', date.toISOString()); // // 把日期以ISO-8601的字符串形式传递
    

    以上两种值传递的方法都可以正确的转换成原生平台的NSDate类型,如果传值错误的话,例如:传递Array类型值,会出现红屏幕的错误。

    随着业务的发展CalendarManager.addEvent的方法会变得越来越复杂,同样参数也会越来越多。很多时候这些参数可能是可选的,在这种情况下面我们需要修改我们的API,可以接受一个事件属性的字典类型,如下代码:

    #import "RCTConvert.h"
     
    RCT_EXPORT_METHOD(addEvent:(NSString *)name details:(NSDictionary *)details)
    {
      NSString *location = [RCTConvert NSString:details[@"location"]];
      NSDate *time = [RCTConvert NSDate:details[@"time"]];
      ...
    }
    

    接下来我们在JavaScript中如下进行调用即可:

    CalendarManager.addEvent('Birthday Party', {
      location: '4 Privet Drive, Surrey',
      time: date.getTime(),
      description: '...'
    })
    

    (三)实战实例

    上面讲了那么多,基本都是概念和流程,现在我们直接演示一个实例让大家看一下,首先看一下封装的原生代码:

    //
    //  CalendarManager.m
    //  ModulesDemo
    //
    //  Created by 江清清 on 16/5/22.
    //  Copyright © 2016年 Facebook. All rights reserved.
    //
     
    #import "CalendarManager.h"
    #import "RCTConvert.h"
    @implementation CalendarManager
    //默认名称
    RCT_EXPORT_MODULE()
    //对外提供调用方法
    RCT_EXPORT_METHOD(addEvent:(NSString *)name location:(NSString *)location){
      NSLog(@"Pretending to create an event %@ at %@", name, location);
    }
    //对外提供调用方法,为了演示事件时间格式化 secondsSinceUnixEpoch
    RCT_EXPORT_METHOD(addEventMore:(NSString *)name location:(NSString *)location data:(NSNumber*)secondsSinceUnixEpoch){
       NSDate *date = [RCTConvert NSDate:secondsSinceUnixEpoch];
    }
    //对外提供调用方法,为了演示事件时间格式化 ISO8601DateString
    RCT_EXPORT_METHOD(addEventMoreTwo:(NSString *)name location:(NSString *)location date:(NSString *)ISO8601DateString)
    {
      NSDate *date = [RCTConvert NSDate:ISO8601DateString];
    }
    //对外提供调用方法,为了演示事件时间格式化 自动类型转换
    RCT_EXPORT_METHOD(addEventMoreDate:(NSString *)name location:(NSString *)location date:(NSDate *)date)
    {
       NSDateFormatter *formatter = [[NSDateFormatter alloc] init] ;
      [formatter setDateFormat:@"yyyy-MM-dd"];
       NSLog(@"获取的事件信息:%@,地点:%@,时间:%@",name,location,[formatter stringFromDate:date]);
    }
     
    //对外提供调用方法,为了演示事件时间格式化 传入属性字段
    RCT_EXPORT_METHOD(addEventMoreDetails:(NSString *)name details:(NSDictionary *) dictionary)
    {
      NSString *location = [RCTConvert NSString:dictionary[@"location"]];
      NSDate *time = [RCTConvert NSDate:dictionary[@"time"]];
      NSString *description=[RCTConvert NSString:dictionary[@"description"]];
      NSLog(@"获取的事件信息:%@,地点:%@,时间:%@,备注信息:%@",name,location,time,description);
     
    }
    @end
    

    前端JS调用方法如下:

    /**
     * Sample React Native App
     * https://github.com/facebook/react-native
     */
    import React,{
      AppRegistry,
      Component,
      StyleSheet,
      Text,
      View,
      TouchableHighlight,
    } from 'react-native';
    ///进行导入NativeModules中的CalendarManger模块
    import { NativeModules } from 'react-native';
    var CalendarManager = NativeModules.CalendarManager;
    class CustomButton extends React.Component {
      render() {
        return (
          <TouchableHighlight
            style={styles.button}
            underlayColor="#a5a5a5"
            onPress={this.props.onPress}>
            <Text style={styles.buttonText}>{this.props.text}</Text>
          </TouchableHighlight>
        );
      }
    }
    class ModulesDemo extends Component {
      render() {
        return (
          <View style={{marginTop:20}}>
            <Text style={styles.welcome}>
                封装iOS原生模块实例
            </Text>
            <CustomButton text="点击调用原生模块addEvent方法"
                onPress={()=>CalendarManager.addEvent('生日聚会', '江苏南通 中天路')}
            />
            <CustomButton text="点击调用原生模块addEvent方法"
                onPress={()=>CalendarManager.addEventMoreDate('生日聚会', '江苏南通 中天路',1463987752)}
            />
            <CustomButton text="调用原生模块addEvent方法-传入字段格式"
                onPress={()=>CalendarManager.addEventMoreDetails('生日聚会', {
                  location:'江苏 南通市 中天路',
                  time:1463987752,
                  description:'请一定准时来哦~'
                })}
            />
          </View>
        );
      }
    }
    const styles = StyleSheet.create({
      welcome: {
        fontSize: 20,
        textAlign: 'center',
        margin: 10,
      },
      button: {
        margin:5,
        backgroundColor: 'white',
        padding: 10,
        borderWidth:1,
        borderColor: '#cdcdcd',
      },
    });
    AppRegistry.registerComponent('ModulesDemo', () => ModulesDemo);
    

    相关文章

      网友评论

        本文标题:React Native 与原生模块数据通信(一)(iOS)

        本文链接:https://www.haomeiwen.com/subject/lcbjjftx.html