翻译自https://facebook.github.io/react-native/docs/native-modules-ios.html

Native Modules

很多情况下,app需要使用原生的api,或者是用一些已经用OC、Swift或C++写好的模块,又或者需要写出更高效率的、或多线程的代码来支撑图像处理、数据库或其它高要求的需求。

React Native的设计当然是支持我们使用原生特性的,以使平台本身的能力得以完全发挥。不过这相对来说是比较进阶的功能,他们的存在虽然是必要的,但在日常开发中并不是必须要用到的。如果RN不支持摸个你想要用到的原生特性,你可以自己做支持。

这是一个高阶教程,介绍了如何搭建一个原生模块。阅读者需要对OC或Swift以及一些核心原生模块(eg.Foundation, UIKit)有一定的了解。

iOS Calendar Module Example

本教程以iOS Calendar Api为例。我们要通过JavaScript来使用iOS calendar。

一个原生模块,就是一个实现了RCTBridgeModule协议的Objective-C类。RCT是ReaCT的缩写。

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

除了要实现RCTBridgeModule协议之外,还需要执行RCT_EXPORT_MODULE()宏指令,它接受的第一个参数,表示这个模块在JavaScript中使用时的引用名。如果没有传这个参数,那默认OC的类名就是js中的引用名。

// CalendarManager.m
@implementation CalendarManager // To export a module named CalendarManager
RCT_EXPORT_MODULE(); // This would name the module AwesomeCalendarManager instead
// RCT_EXPORT_MODULE(AwesomeCalendarManager); @end

如果需要在js中使用CalendarManager中的方法,需要使用RCT_EXPORT_METHOD()宏指令,将方法暴露出去。

#import "CalendarManager.h"
#import <React/RCTLog.h> @implementation CalendarManager RCT_EXPORT_MODULE(); RCT_EXPORT_METHOD(addEvent:(NSString *)name location:(NSString *)location)
{
RCTLogInfo(@"Pretending to create an event %@ at %@", name, location);
}

暴露出去之后,在js文件中调用该方法的方式如下:

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

注:JavaScript中的方法名称

暴露到JavaScript中的原生方法的方法名就是原生方法名第一个冒号前的内容。另外,RN定义了一个宏指令RCT_REMAP_METHOD(),可以用来制定方法在JavaScript中的方法名。当原生代码中暴露出去的不同的方法的方法名,第一个逗号前有相同的内容时,在JavaScript中方法名就会冲突,这个宏指令就用得上了。

暴露的原生方法的返回值类型只能是void,因为React Native的bridge是异步的,所以向JavaScript传递原生方法的调用结果的方式只能通过回调函数或注册事件的方式。

Argument Types

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

  • string (NSString)
  • number (NSIntegerfloatdoubleCGFloatNSNumber)
  • boolean (BOOLNSNumber)
  • array (NSArray) of any types from this list
  • object (NSDictionary) with string keys and values of any type from this list
  • function (RCTResponseSenderBlock)

同时也支持RCTConvert class支持的类型。RCTConvert的helper functions接收JSON值,将其转化为Objective-C的类型或类。

在我们CalendarManager的例子中,我们想要将日期传给远胜方法,但是我们不能直接传js的Date类型的对象,我们需要将data类型的对象转换成字符串或数字。原生方法可以这样写:

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];
}

不过我们也可以使用自动类型转换,省去手动转换的步骤:

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

在js中调用的方式如下:

CalendarManager.addEvent('Birthday Party', '4 Privet Drive, Surrey', date.getTime()); // passing date as number of seconds since Unix epoch
// or
CalendarManager.addEvent('Birthday Party', '4 Privet Drive, Surrey', date.toISOString()); // passing date as ISO-8601 string

这两种调用都会使原生发放得到正确的NSDate类型的对象。如果是一个不合法的类型,你会看到红盒子的错误信息。

如果CalendarManager.addEvent方法变得越来越复杂,参数的数量会越来越多,而且不是每一个参数都是必要的。这时候就可以考虑,通过字典的形式传入参数。如下:

#import <React/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: '...'
})

注:关于array和map

Objective-C不会限制这两种数据结构里的内容的类型。如果原生模块需要一个字符串的数组,而在js中调用的时候,传入了包含数字和字符串的数组,我们在原生方法中会获得既有NSNumber又有NSString类型的NSArray。对于数组,RCTConvert提供了一些约束类型的数据结构永远方法的声明中,例如NSStringArray,UIColorArray。对于maps,开发者需要利用RCTConvert的方法分别取去检查里面的值类型。

Callbacks

原生模块可以接收一个特别的参数:一个js回调函数,作为原生函数执行过程中或执行结束后,向js返回结果并通过js进行进一步处理的方法。

RCT_EXPORT_METHOD(findEvents:(RCTResponseSenderBlock)callback)
{
NSArray *events = ...
callback(@[[NSNull null], events]);
}

RCTResponseSenderBlock只接收一个参数,一个传给js回调函数的参数数组。在我们的例子中,我们的第一个参数是错误信息,作为回调函数参数数组的第一个元素。

CalendarManager.findEvents((error, events) => {
if (error) {
console.error(error);
} else {
this.setState({events: events});
}
})

一个原生模块,应该在方法中立即调用回调函数。也可以将回调函数存下来之后再调用,这常常用在委托中,RCTAlertManager就是一个例子。如果回调函数不被出发,将会造成内存泄漏。如果onSuccess和onFail回调同时存在,只应该调用其中一个。

如果想要传递错误信息对象到js中,用RCTUtils.h提供的RCTMakeError。目前它向js传入了一个error-shaped字典,不过未来会自动生成真正的js的Error对象。

react native 学习之 native modules的更多相关文章

  1. React Native 学习-01

    React Native 学习 (学习版本 0.39) 一.环境配置 二.IDE选择 webstorm 1.webstorm配置 ①.首先是可以选择使用汉化包汉化.eu68 ②.安装插件和外部库. 由 ...

  2. react native 学习一(环境搭配和常见错误的解决)

    react native 学习一(环境搭配) 首页,按照http://reactnative.cn/docs/0.30/getting-started.html#content上的介绍,下载安装pyt ...

  3. React Native 学习资料

    React Native 学习资料 学习资料 网址 React Native中文网 https://reactnative.cn/

  4. React Native 学习(三)之 FlexBox 布局

    React Native 学习(三)之 FlexBox 布局

  5. React Native 学习笔记--进阶(二)--动画

    React Native 进阶(二)–动画 动画 流畅.有意义的动画对于移动应用用户体验来说是非常必要的.我们可以联合使用两个互补的系统:用于全局的布局动画LayoutAnimation,和用于创建更 ...

  6. node-webkit学习(4)Native UI API 之window

    node-webkit学习(4)Native UI API 之window 文/玄魂 目录 node-webkit学习(4)Native UI API 之window 前言 4.1  window a ...

  7. node-webkit学习(3)Native UI API概览

    node-webkit学习(3)Native UI API概览 文/玄魂 目录 node-webkit学习(3)Native UI API概览 前言 3.1  Native UI api概览 Exte ...

  8. 【优质】React的学习资源

    React的学习资源 github 地址: https://github.com/LeuisKen/react-collection https://github.com/reactnativecn/ ...

  9. (转)2019年 React 新手学习指南 – 从 React 学习线路图说开去

    原文:https://www.html.cn/archives/10111 注:本文根据 React 开发者学习线路图(2018) 结构编写了很多新手如何学习 React 的建议.2019 年有标题党 ...

随机推荐

  1. 深入浅出 JMS(三) - ActiveMQ 消息传输

    深入浅出 JMS(三) - ActiveMQ 消息传输 一.消息协商器(Message Broker) broke:消息的交换器,就是对消息进行管理的容器.ActiveMQ 可以创建多个 Broker ...

  2. word2vec相关资源

    word2vec官网:https://code.google.com/p/word2vec/ 利用中文数据跑Google开源项目word2vec:http://www.cnblogs.com/hebi ...

  3. KBMMW 4.81.00 发布

    这次更新的速度非常快. 4.81.00 May 9 2015 Important notes (changes that may break existing code) ============== ...

  4. 2018.07.17 洛谷P1368 工艺(最小表示法)

    传送门 好的一道最小表示法的裸板,感觉跑起来贼快(写博客时评测速度洛谷第二),这里简单讲讲最小表示法的实现. 首先我们将数组复制一遍接到原数组队尾,然后维护左右指针分别表示两个即将进行比较的字符串的头 ...

  5. 2018.07.10NOIP模拟 Knapsack(单调队列优化dp)

    Knapsack 题目背景 SOURCE:NOIP2016-RZZ-4 T2 题目描述 有 n 个物品,第 i 个物品的重量为 ai . 设 f(i,j,k,l,m) 为满足以下约束的物品集合数量: ...

  6. spark 写 hbase 数据库,遇到Will not attempt to authenticate using SASL (unknown error)

    今日在windows上用spark写hbase的函数 saveAsHadoopDataset 写hbase数据库的时候,遇到Will not attempt to authenticate using ...

  7. centOS下NFS服务器的安装配置详解

    一.NFS简介 NFS就是Network FileSystem的缩写,最早之前是由Sun公司所发展出来的.他最大的功能就是可以透过网络,让不同的机器.不同的操作系统可以彼此分享个别档案(share f ...

  8. python编码(五)

    说说区位码.GB2312.内码和代码页 目前Windows的内核已经采用Unicode编码,这样在内核上可以支持全世界所有的语言文字.但是由于现有的大量程序和文档都采用了某种特定语言的编码,例如GBK ...

  9. spring AbstractRoutingDataSource实现动态数据源切换

    使用Spring 提供的 AbstractRoutingDataSource 实现 创建 AbstractRoutingDataSource 实现类,负责保存所有数据源与切换数据源策略:public ...

  10. Linux常见配置文件

    一./etc 配置文件 /etc/passwd 用户数据库,其中的域给出了用户名.真实姓名.家目录.加密口令和用户的其他信息 /etc/group 类似/etc/passwd ,但说明的不是用户而是组 ...