React Native填坑之旅--与Native通信之iOS篇
终于开始新一篇的填坑之旅了。RN厉害的一个地方就是RN可以和Native组件通信。这个Native组件包括native的库和自定义视图,我们今天主要设计的内容是native库方面的只是。自定义视图的使用会在后面讲到。 坑是什么样的坑主要的是遇到一个业务需求,需要检测当前应用的版本是什么。需要返回当前的版本号和build数。 主要的需求在native来说非常简单: NSString * version = [[NSBundle mainBundle] objectForInfoDictionaryKey: @"CFBundleShortVersionString"];
NSString * build = [[NSBundle mainBundle] objectForInfoDictionaryKey: (NSString *)kCFBundleVersionKey];
两句分别获得了版本号和build数。 开始填坑填坑其实也是意外的简单。当然,我们不准备把这个代码作为库发布到npm上给别人用,所以复杂度自然降低了不少。 首先、在Xcode里创建 在RNUpgrade.h头文件中,添加 #import <Foundation/Foundation.h>
#import "RCTBridgeModule.h"
@interface RNUpgrade : NSObject<RCTBridgeModule>
@end
之后对于头文件就可以什么都不用管了。至少对于暴露接口这件事是这样的。 下面就来看源文件吧。看文档,要暴露native方法就必须在源文件里包含一个宏的调用,这个宏是: #import "RNUpgrade.h"
#import "RCTUtils.h"
#import "AppDelegate.h"
NSString *const RNUPGRADE_ERROR_DOMAIN = @"Upgrade info error";
@implementation RNUpgrade
RCT_EXPORT_MODULE();
@end
那么如何来暴露出一个方法呢?使用 RCT_EXPORT_METHOD(addEvent:(NSString *)name location:(NSString *)location)
{
RCTLogInfo(@"Pretending to create an event %@ at %@",name,location);
}
宏 RCT_EXPORT_METHOD(getCurrentInfo) {
@try {
NSString * version = [[NSBundle mainBundle] objectForInfoDictionaryKey: @"CFBundleShortVersionString"];
NSString * build = [[NSBundle mainBundle] objectForInfoDictionaryKey: (NSString *)kCFBundleVersionKey];
return @{@"versionName": version,@"versionCode": build}
} @catch (NSException *exception) {
//Log error info...
}
}
但是,如何返回字典呢?直接return?接着差文档。 暴露给RN的方法是不能直接返回任何东西的。因为RN的调用时异步的,所以只能使用回调的方式,或者触发事件的方式实现返回值。 回调!看个官网的例子: RCT_EXPORT_METHOD(findEvents:(RCTResponseSenderBlock)callback)
{
NSArray *events = ...
callback(@[[NSNull null],events]);
}
好的,回调就说到这里了。因为笔者的项目已经上了 RCT_EXPORT_METHOD(getCurrentInfo:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject) {
@try {
NSString * version = [[NSBundle mainBundle] objectForInfoDictionaryKey: @"CFBundleShortVersionString"];
NSString * build = [[NSBundle mainBundle] objectForInfoDictionaryKey: (NSString *)kCFBundleVersionKey];
resolve(@{@"versionName": version,@"versionCode": build});
} @catch (NSException *exception) {
NSError *error = [NSError errorWithDomain:RNUPGRADE_ERROR_DOMAIN code:1 userInfo: exception.userInfo];
reject(exception.name,exception.reason,error);
}
}
于是,这样就可以返回一个 在RN的项目里调用这个方法: // 首先通过`NativeModules`接收暴露的native模块。
import { NativeModules } from "react-native"
const upgrade = NativeModules.RNUpgrade
// 方法调用
const ret = await Promise.all([upgrade.getCurrentInfo(),upgrade.getUpgradeInfo()])
没错,模块还有另外一个native方法。这个native方法也返回一个 返回声明相同的native方法其实在native模块里很多方法的声明都是一模一样的: RCT_REMAP_METHOD(getCurrentInfo,resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject) {
@try {
NSString * version = [[NSBundle mainBundle] objectForInfoDictionaryKey: @"CFBundleShortVersionString"];
NSString * build = [[NSBundle mainBundle] objectForInfoDictionaryKey: (NSString *)kCFBundleVersionKey];
resolve(@{@"versionName": version,error);
}
}
基本上在项目里如何暴露一个native方法给RN的js调用非常简单,就如上面所述一样。 重要的一点:线程在文档中有这么一点:多线程。千万不要根据RN实现的一些细节就假设你的模块运行在某某线程上。官网也说了,这个是会变的。如果你要确定你的代码运行在什么线程上,通过方法 注意:指定的methodQueue会被你模块里的所有方法共享。 如果运行在主线程上: - (dispatch_queue_t)methodQueue { return dispatch_get_main_queue(); }
如果运行在自己创建的线程上: - (dispatch_queue_t)methodQueue { return dispatch_queue_create("com.facebook.React.AsyncLocalStorageQueue",DISPATCH_QUEUE_SERIAL); }
如果模块里只有小部分代码运行在其他的线程上,可以使用native里传统的方法 RCT_EXPORT_METHOD(doSomethingExpensive:(NSString *)param callback:(RCTResponseSenderBlock)callback)
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0),^{
// 在这里执行长时间的操作 ...
// 你可以在任何线程/队列中执行回调函数
callback(@[...]);
});
}
而且:**methodQueue 省心省力! 填坑完毕!(编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |