1.前言
上一篇文章介绍封装Android原生模块,今天将介绍如何封装ios原生模块供React native调用。在React Native中,ios平台原生模块是一个实现了RCTBridgeModule协议的Objective-C类,其中RCT是ReaCT的缩写。这里会涉及到一些Objective-C的代码编写,不过不用担心,有一定的ios开发的基础当然更好,如果你之前从未接触使用过Objective-C,可能有些语法理解起来较为困难,但这并不妨碍阅读,本文用编程世界里最经典的HelloWorld实例来介绍ios原生模块的封装。但如果以后想封装更为复杂的ios原生平台模块,掌握Objective-C或者Swift的语法还是必须的。
2.创建模块类
在..\ExerciseProject\ios\ExerciseProject
目录下新建HelloWorld.h和HelloWorl.m文件,或使用Xcode打开..\ExerciseProject\ios\ExerciseProject.xcodeproj
文件,新建HelloWorld类,会自动生成HelloWorld.h和HelloWorl.m文件。.h头文件包含了类的接口,.m文件中实现了具体的功能。首先看HelloWorld.h:
#import
@interface HelloWorld : NSObject
@end
这里导入了RCTBridgeModule.h头文件,RCTBridgeModule是React Native提供的与iOS原生通信的桥接实现。接着定义了HelloWorld类继承于NSObject并实现了RCTBridgeModule接口。
再来看HelloWorld.m:
#import "HelloWorld.h"
#import "RCTLog.h"
@implementation HelloWorld
RCT_EXPORT_MODULE();
RCT_EXPORT_METHOD(hello:(NSString *)name)
{
RCTLogInfo(@"Hello %@ from IOS", name);
}
@end
第一行导入了相应的.h文件,由于后面需要使用RCTLogInfo输出日志到应用台,这里导入了RCTLog.h。@implementation与@end之间是HelloWorld的具体实现。通过RCT_EXPORT_MODULE()宏导出此原生模块供React Native接口调用,而RCT_EXPORT_METHOD()宏来实现要给Javascript使用的方法。
3.在Javascript端使用
为了使当前封装的模块在Javascript中可以方便使用,通常会把该原生模块封装成Javascript模块。这样可以省下每次访问组件都要加入NativeModules的步骤,不过该步骤不是必须的。
本例中在\ExerciseProject\src\12_nativeapi\
中新建一个HelloWorldIOS.js文件
let { NativeModules } = require('react-native');
module.exports = NativeModules.HelloWorld;
然后在别的Javascript文件代码中就可以如下进行访问:
import HelloWorldIOS from './HelloWorldIOS';
...
HelloWorldIOS.hello('Jack');
具体在Javascript文件中的调用方法实例代码如下:
import React, { Component } from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View,
} from 'react-native';
import HelloWorldIOS from './HelloWorldIOS';
export default class HelloWorldIOSDemo extends Component {
onPress(){
HelloWorldIOS.hello('Jack');
}
render() {
return (
<View style={{marginTop:40}}>
<Button onPress={this.onPress.bind(this)} title="Hello" />
View>
);
}
}
4.参数类型说明
上面例子中我们传入了string类型的参数,实际应用中还可能需要各种类型的参数。RCT_EXPORT_METHOD 支持所有标准JSON类型。具体如下:
- string (NSString)
- number (NSInteger, float, double, CGFloat, NSNumber)
- boolean (BOOL, NSNumber)
- array (NSArray) 包含本列表中任意类型
- object (NSDictionary) 包含string类型的键和本列表中任意类型的值
- function (RCTResponseSenderBlock)
除此以外,任何RCTConvert类支持的类型也都可以使用。RCTConvert是React Native提供的一个类型转换库。例如在上面提到的支持的类型中没有时间类型,如果我们需要传递时间即可通过RCTConvert进行类型转换。
RCTConvert的使用具体可见下例:
步骤如前面2、3小节,先创建DateIOS.h
#import
#import
@interface DateIOS : NSObject<RCTBridgeModule>
@end
再创建DateIOS.m
#import "DateIOS.h"
@implementation DateIOS
RCT_EXPORT_MODULE();
RCT_REMAP_METHOD(compareDate, date1:(nonnull NSNumber *)d1 date2:(nonnull NSNumber *)d2)
{
NSDate* dt1 = [RCTConvert NSDate:d1];
NSDate* dt2 = [RCTConvert NSDate:d2];
NSComparisonResult result = [dt1 compare:dt2];
switch(result){
case NSOrderedAscending:
{
NSLog(@"compare result %@",@"start time );
}
case NSOrderedDescending:
{
NSLog(@"compare result %@",@"start time > end time");
}
}
}
@end
在Javascript端使用
import React, { Component } from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View,
Button,
DatePickerIOS,
NativeModules
} from 'react-native';
export default class DateIOSDemo extends Component {
constructor(){
super();
this.state = {startDate: new Date(), endDate: new Date()};
}
onPressDateCompare() {
let dateCompare = NativeModules.DateIOS;
dateCompare.printDate(this.state.startDate.getTime(), this.state.endDate.getTime());
}
onStartDateChange(date) {
this.setState({startDate: date});
}
onEndDateChange(date) {
this.setState({endDate: date});
}
render() {
return (
<View>
<DatePickerIOS
date={this.state.startDate}
mode='date'
onDateChange={this.onStartDateChange.bind(this)} />
<DatePickerIOS
date={this.state.endDate}
mode='date'
onDateChange={this.onEndDateChange.bind(this)} />
<Button onPress={this.onPressDateCompare.bind(this)} title="Compare Date" />
View>
);
}
}
上例中由于不能在桥接通道里传递Date对象,所以需要把日期转化成字符串或数字来传递。在ios端通过如下方式进行转换:
NSDate* dt1 = [RCTConvert NSDate:d1];
在Javascript端将日期以unix时间戳形式传递:
let dateCompare = NativeModules.DateIOS;
dateCompare.printDate(this.state.startDate.getTime(), this.state.endDate.getTime());
也可以以ISO-8601的字符串形式传递:
let dateCompare = NativeModules.DateIOS
dateCompare.printDate(this.state.startDate.toISOString(), this.state.endDate.toISOString())
这两种方式都会被转换为正确的NSDate类型。但如果提供一个不合法的值,例如一个Array,则程序会报错。
以上是关于iOS原生模块的封装与调用的一个简单介绍。