翻译自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的缩写。

  1. // CalendarManager.h
  2. #import <React/RCTBridgeModule.h>
  3.  
  4. @interface CalendarManager : NSObject <RCTBridgeModule>
  5. @end

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

  1. // CalendarManager.m
  2. @implementation CalendarManager
  3.  
  4. // To export a module named CalendarManager
  5. RCT_EXPORT_MODULE();
  6.  
  7. // This would name the module AwesomeCalendarManager instead
  8. // RCT_EXPORT_MODULE(AwesomeCalendarManager);
  9.  
  10. @end

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

  1. #import "CalendarManager.h"
  2. #import <React/RCTLog.h>
  3.  
  4. @implementation CalendarManager
  5.  
  6. RCT_EXPORT_MODULE();
  7.  
  8. RCT_EXPORT_METHOD(addEvent:(NSString *)name location:(NSString *)location)
  9. {
  10. RCTLogInfo(@"Pretending to create an event %@ at %@", name, location);
  11. }

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

  1. import { NativeModules } from 'react-native';
  2. var CalendarManager = NativeModules.CalendarManager;
  3. 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类型的对象转换成字符串或数字。原生方法可以这样写:

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

或者这样:

  1. RCT_EXPORT_METHOD(addEvent:(NSString *)name location:(NSString *)location date:(NSString *)ISO8601DateString)
  2. {
  3. NSDate *date = [RCTConvert NSDate:ISO8601DateString];
  4. }

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

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

在js中调用的方式如下:

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

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

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

  1. #import <React/RCTConvert.h>
  2.  
  3. RCT_EXPORT_METHOD(addEvent:(NSString *)name details:(NSDictionary *)details)
  4. {
  5. NSString *location = [RCTConvert NSString:details[@"location"]];
  6. NSDate *time = [RCTConvert NSDate:details[@"time"]];
  7. ...
  8. }

在JavaScript中调用:

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

注:关于array和map

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

Callbacks

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

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

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

  1. CalendarManager.findEvents((error, events) => {
  2. if (error) {
  3. console.error(error);
  4. } else {
  5. this.setState({events: events});
  6. }
  7. })

一个原生模块,应该在方法中立即调用回调函数。也可以将回调函数存下来之后再调用,这常常用在委托中,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. Flask源码剖析详解

    1. 前言 本文将基于flask 0.1版本(git checkout 8605cc3)来分析flask的实现,试图理清flask中的一些概念,加深读者对flask的理解,提高对flask的认识.从而 ...

  2. 品味性能之道<三>:方法论

    自顶向下的性能优化方法论 系统优化是包括系统设计.开发.产品上线.平台优化的全过程,不同阶段的优化工作对全系统所带来的效益是不同的.理想的性能优化论应该采用自顶向下的优化方法,即在项目设计.开发和上线 ...

  3. cmake 头文件 库文件 链接库

    原文地址:http://www.cnblogs.com/binbinjx/p/5626916.html 1. 添加头文件目录INCLUDE_DIRECTORIES 语法: include_direct ...

  4. 深入应用c++11 随书代码

    代码并未在作者github上提供 将书中代码敲至vc 并调试运行 依赖BOOST库 编译环境vs2015 boost1.59 // Client.cpp : 定义控制台应用程序的入口点. // #in ...

  5. pyqt5和qtdesign的使用

    http://blog.csdn.net/Angelasan/article/details/44917283 发现我的使用时候有点跟他不同. 我是 g: utf- -*- # Form implem ...

  6. Sublime Text webstorm等编译器快速编写HTML/CSS代码的技巧

    <!DOCTYPE html> Sublime Text webstorm等编译器快速编写HTML/CSS代码的技巧--summer-rain博客园 xiayuhao 东风夜放花千树. 博 ...

  7. 【httpclient-4.3.1.jar】httpclient发送get、post请求以及携带数据上传文件

    1.发送get.post携带参数以及post请求接受JSON数据: package cn.qlq.utils; import java.io.BufferedReader; import java.i ...

  8. 2018.07.23 codeforces 438D. The Child and Sequence(线段树)

    传送门 线段树维护区间取模,单点修改,区间求和. 这题老套路了,对一个数来说,每次取模至少让它减少一半,这样每次单点修改对时间复杂度的贡献就是一个log" role="presen ...

  9. spring cloud--------------------HystrixCommand使用

    一.注解使用: (一)注解同步执行 1.注解开启断路器功能 @EnableCircuitBreaker 2.方法事例 @HystrixCommand(fallbackMethod = "er ...

  10. Word插入圆圈数字

    https://wenku.baidu.com/view/3260a2f0a1c7aa00b52acb5a.html Word 中在对应位置输入四位字符,选中字符(如:选中 2473 ),按 Alt+ ...