翻译自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. Spring.NET学习笔记7——依赖对象的注入(基础篇) Level 200

    1.person类 public class Person    {        public string Name { get; set; }        public int Age { g ...

  2. Tomcat的杂七杂八

    localhost_access_log.2016-01-15.txt  原来这里面有访问记录. /logs/catalina.2016-01-22.log 这里有显示失败的信息 2016-01-23 ...

  3. 乞丐版servlet容器第4篇

    6. NIOConnector 现在为Server添加NIOConnector,添加之前可以发现我们的代码其实是有问题的.比如现在的代码是无法让服务器支持同时监听多个端口和IP的,如同时监听 127. ...

  4. linux安装mysql~~~mysql5.6.12

    Linux安装mysql服务器 准备: MySQL-client-5.6.12-1.rhel5.i386.rpm MySQL-server-5.6.12-1.rhel5.i386.rpm 首先检查环境 ...

  5. HDU 1040 As Easy As A+B (排序。。。水题)

    题意:给定n个数,让你从小到大排序. 析:不说什么了. 代码如下: #include <cstdio> #include <iostream> #include <cst ...

  6. SpringMVC上传图片总结(2)--- 使用百度webuploader上传组件进行上传图片

    SpringMVC上传图片总结(2)--- 使用百度webuploader上传组件进行上传图片   在上一篇文章中,我们介绍了< SpringMVC上传图片的常规上传方法 >.本文接着第一 ...

  7. D3_book 11.3 force

    <!-- pie example --> <!DOCTYPE html> <meta charset="utf-8"> <style> ...

  8. [FRAMESET][PHP]Frameset下面使用php-header('location:...') redirect链接

    一般,我们的管理后台都是使用frameset来进行布局的,所以如果我们对后台的登录会话时间进行了设定,那么在超过该时间session失效之后,那么我们就必须要在php文件中进行判断处理. 判断会话失效 ...

  9. ECG心电图数据1

    最近在写一篇基于小波变换的ECG信号压缩算法的论文,遇到了怎样获取ECG信号测试数据的问题,在百度和专业论坛里搜索了一番,发现也有很多朋友为此发愁.现在论文写好了,投稿中,顺便也把怎样获取和处理ECG ...

  10. 通过keepalived搭建MySQL双主模式的高可用集群系统

    1. 配置MySQL双主模式 1.修改my.cnf配置文件 默认情况下,MySQL的配置文件是/etc/my.cnf,在配置文件的[mysqld]段添加如下内容: server-id=1 log-bi ...