前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >玩转RN:IOS如何导出原生模块并在js中调用

玩转RN:IOS如何导出原生模块并在js中调用

作者头像
腾讯IVWEB团队
发布2020-06-28 00:15:23
2K0
发布2020-06-28 00:15:23
举报

写在前面

React Native 的宗旨是『Learn once, write anywhere.』,换句话说,在 RN 开发中,大多数时候,开发者并不需要关心 native 那一层,安心编写react组件以及相应的业务逻辑就可以了。

不过有的时候,也会需要用到原生的模块,比如:

  • 高性能计算:图片处理、文件压缩等;
  • 复用native已有的模块:比如跨Android、IOS的链接库等;
  • RN 尚不支持的native模块:比如iOS SDK更新吼,RN可能还没有对应的模块;

这种情况下,可以利用 RN 导出原生模块给 js 调用,下文会简单举例说明。文中例子可以在 笔者的Github 上找到,也建议查看官方文档获取更详细说明。

自定义原生模块

首先,创建头文件 TodoList.h,TodoList 实现了 RCTBridgeModule 协议。

代码语言:javascript
复制
// TodoList.h
#import <React/RCTBridgeModule.h>

@interface TodoList : NSObject <RCTBridgeModule>

@end

接着,创建 TodoList.m。代码简单解释下:

  • RCT_EXPORT_MODULE();:将 TodoList 模块导出;
  • RCT_EXPORT_METHOD(add):导出 add 方法,:后是参数列表(可多个);
代码语言:javascript
复制
// TodoList.m
#import "TodoList.h"
#import <React/RCTLog.h>

@implementation TodoList

NSMutableArray *list = nil;

RCT_EXPORT_MODULE();

RCT_EXPORT_METHOD(create)
{
    list = [[NSMutableArray alloc] init];
}

RCT_EXPORT_METHOD(add:(NSString *)item)
{
    NSLog(@"add: %@", item);
    [list addObject:@{ @"desc":item, @"done": @0 }];
}

从js中调用

从 js 中调用自定义的原生模块非常简单,代码如下:

代码语言:javascript
复制
import {NativeModules} from 'react-native';

const TodoList = NativeModules.TodoList;
TodoList.create();
TodoList.add('起床');

xcode中打印日志:

代码语言:javascript
复制
2019-11-08 21:24:32.636547+0800 RNTest[5027:50749999] add: 起床

函数回调

在前端开发中,函数回调非常常见,RN 中导出的原生方法,也支持传入回调方法,如下所示。

代码语言:javascript
复制
// 例子:方法导出,支持回调
RCT_EXPORT_METHOD(addWithCallback:(NSString *)item callback:(RCTResponseSenderBlock)callback)
{
    NSLog(@"addWithCallback: %@", item);
    
    [list addObject:@{ @"desc":item, @"done": @0 }];
    callback(@[[NSNull null], list]);
}

在 index.js 中新增调用:

代码语言:javascript
复制
// 例子:函数回调
TodoList.addWithCallback('吃早餐', (error, list) => {
  if (error === null) {
    console.log(`[addWithCallback] list.length == ${list.length}`);
  }      
});

输出如下:

代码语言:javascript
复制
[addWithCallback] list.length == 2

事件触发

调用原生模块,除了函数回调,Native Module 还可以主动抛出事件,在 js 层进行监听处理,例子如下。

首先,对 TodoList.h 进行修改,继承 RCTEventEmitter

代码语言:javascript
复制
// TodoList.h
#import <React/RCTBridgeModule.h>
#import <React/RCTEventEmitter.h>

@interface TodoList : RCTEventEmitter <RCTBridgeModule>

@end

接着,导出 addAndTriggerEvent 方法,里面主动抛出 ItemAdded 事件。注意,通过 sendEventWithName 抛出的事件需要在 supportedEvents 里进行注册,不然运行期会报错。

代码语言:javascript
复制
// TodoList.m

// 返回的数组为支持的事件名列表
- (NSArray<NSString *> *)supportedEvents
{
    return @[@"ItemAdded"];
}

// 例子:事件抛出
RCT_EXPORT_METHOD(addAndTriggerEvent:(NSString *)item)
{
    NSLog(@"addAndTriggerEvent: %@", item);
    
    [list addObject:@{ @"desc":item, @"done": @0 }];
    [self sendEventWithName:@"ItemAdded" body:list];
}

最后,在 index.js 监听 ItemAdded 事件,搞定。

代码语言:javascript
复制
const TodoList = NativeModules.TodoList;
const todoListEmitter = new NativeEventEmitter(TodoList);

TodoList.create();

// 监听事件
todoListEmitter.addListener('ItemAdded', list => {
  console.log(`[ItemAdded] list.length == ${list.length}`);
});

// 添加item,在这个方法里会抛出事件
TodoList.addAndTriggerEvent('上班');

输出如下:

代码语言:javascript
复制
[ItemAdded] list.length == 3

返回Promise实例

对于异步操作来说,返回 Promise 实例通常是个不错的选择,RN原生函数 也进行了支持了这个特性。

首先,导出方法 addAndReturnPromise,这个方法接收两个参数 item、isResolved,根据 isResolved 的值决定 Promise 实例最终的状态。

代码语言:javascript
复制
// 例子:返回Promise实例
RCT_REMAP_METHOD(addAndReturnPromise,
                 item:(NSString *) item
                 ifResolved:(int) ifResolved
                 findEventsWithResolver:(RCTPromiseResolveBlock)resolve
                 rejecter:(RCTPromiseRejectBlock)reject)
{
    NSLog(@"addAndTriggerEvent: %@", item);

    [list addObject:@{ @"desc":item, @"done": @0 }];
    
    if (ifResolved) {
        resolve([NSString stringWithFormat:@"%@ is accepted.", item]);
    } else {
        NSString *domain = @"com.chyingp.www";
        NSDictionary *userInfo = @{ @"desc": @"nonsense" };
        NSInteger code = -1;
        NSError *error =[NSError errorWithDomain:domain code:code userInfo:userInfo];
        NSString *errMsg = [NSString stringWithFormat:@"%@ is not accepted.", item];
        
        reject(@"ErrorFromNativeModule", errMsg, error);
    }
}

其次,在 index.js 中添加调用

代码语言:javascript
复制
// 例子:返回Promise实例
const resolver = msg => console.log(`[addAndReturnPromise] [resolved] ${msg}`);
const rejecter = error => console.log(`[addAndReturnPromise] [rejected] ${error.message}`);
    
TodoList.addAndReturnPromise('加班', true).then(resolver).catch(rejecter);
TodoList.addAndReturnPromise('休息', false).then(resolver).catch(rejecter);

输出如下(有点悲伤):

代码语言:javascript
复制
[addAndReturnPromise] [resolved] 加班 is accepted.
[addAndReturnPromise] [rejected] 休息 is not accepted.

相关链接

2019.11.07-calling-native-module-from-js-in-rn/

https://facebook.github.io/react-native/docs/native-modules-ios

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 写在前面
  • 自定义原生模块
  • 从js中调用
  • 函数回调
  • 事件触发
  • 返回Promise实例
  • 相关链接
相关产品与服务
图片处理
图片处理(Image Processing,IP)是由腾讯云数据万象提供的丰富的图片处理服务,广泛应用于腾讯内部各产品。支持对腾讯云对象存储 COS 或第三方源的图片进行处理,提供基础处理能力(图片裁剪、转格式、缩放、打水印等)、图片瘦身能力(Guetzli 压缩、AVIF 转码压缩)、盲水印版权保护能力,同时支持先进的图像 AI 功能(图像增强、图像标签、图像评分、图像修复、商品抠图等),满足多种业务场景下的图片处理需求。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档