ReactBridge
提供 Swift 宏,用于 React Native 向 JavaScript 暴露原生模块和 UI 组件。
将 @ReactModule
宏附加到您的类定义,它将导出并注册原生模块类到 React Native,这将允许您从 JavaScript 访问其代码。
import React
import ReactBridge
@ReactModule
class CalendarModule: NSObject, RCTBridgeModule {
}
注意
ReactBridge
需要导入React
库。Swift 类必须继承自NSObject
并且必须遵循RCTBridgeModule
协议。
@ReactModule
宏还接受可选的 jsName
参数,用于指定在您的 JavaScript 代码中可以访问的名称。
@ReactModule(jsName: "Calendar")
class CalendarModule: NSObject, RCTBridgeModule {
}
注意 如果您未指定名称,则 JavaScript 模块名称将与 Swift 类名匹配。
现在可以通过以下方式在 JavaScript 中访问原生模块
import { NativeModules } from 'react-native';
const { Calendar } = NativeModules;
方法
除非明确告知,否则 React Native 不会将原生模块中的任何方法暴露给 JavaScript。这可以使用 @ReactMethod
宏来完成。
@ReactModule(jsName: "Calendar")
class CalendarModule: NSObject, RCTBridgeModule {
@ReactMethod
@objc func createEvent(title: String, location: String) {
print("Create event '\(title)' at '\(location)'")
}
}
注意 导出的方法必须使用
@objc
属性标记。
现在您已经拥有了原生模块,您可以调用您的原生方法 createEvent()
Calendar.createEvent('Wedding', 'Las Vegas');
回调
默认情况下,用 @ReactMethod
宏标记的方法是异步的,但如果需要将数据从 Swift 传递到 JavaScript,您可以使用 RCTResponseSenderBlock
类型的回调参数。
@ReactMethod
@objc func createEvent(title: String, location: String, callback: RCTResponseSenderBlock) {
print("Create event '\(title)' at '\(location)'")
let eventId = 10;
callback([eventId])
}
然后可以使用以下方法在 JavaScript 中访问此方法
Calendar.createEvent('Wedding', 'Las Vegas', eventId => {
console.log(`Created a new event with id ${eventId}`);
});
Promise
原生模块也可以实现 Promise,这可以简化您的 JavaScript,尤其是在使用 async/await 语法时。
@ReactMethod
@objc func createEvent(title: String, location: String, resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) {
do {
let eventId = try createEvent(title: title, location: location)
resolve(eventId)
}
catch let error as NSError {
reject("\(error.code)", error.localizedDescription, error)
}
}
此方法的 JavaScript 对应项返回一个 Promise
Calendar.createEvent('Wedding', 'Las Vegas')
.then(eventId => {
console.log(`Created a new event with id ${eventId}`);
})
.catch(error => {
console.error(`Error: ${error}`);
});
继承
可以继承其他原生模块(实现 RCTBridgeModule
协议)并覆盖现有功能或添加附加功能。例如,要向 JavaScript 发送事件,您可以继承 RCTEventEmitter
@ReactModule
class EventEmitter: RCTEventEmitter {
static private(set) var shared: EventEmitter?
override init() {
super.init()
Self.shared = self
}
override func supportedEvents() -> [String]! {
["EventReminder"]
}
func sendReminderEvent(title: String) {
sendEvent(withName: "EventReminder", body: ["title" : title])
}
}
...
EventEmitter.shared?.sendReminderEvent(title: "Dinner Party")
然后在 JavaScript 中,您可以使用您的模块创建 NativeEventEmitter
并订阅特定事件
const { EventEmitter } = NativeModules;
this.eventEmitter = new NativeEventEmitter(EventEmitter);
this.emitterSubscription = this.eventEmitter.addListener('EventReminder', event => {
console.log(event); // Prints: { title: 'Dinner Party' }
});
有关原生模块的更多详细信息,请参见:https://reactnative.net.cn/docs/native-modules-ios。
要暴露原生视图,您应该将 @ReactView
宏附加到 RCTViewManager
的子类,该子类通常也是视图的委托,通过桥接将事件发送回 JavaScript。
import React
import ReactBridge
import MapKit
@ReactView
class MapView: RCTViewManager {
override func view() -> UIView {
MKMapView()
}
}
然后您需要一些 JavaScript 来使其成为可用的 React 组件
import {requireNativeComponent} from 'react-native';
const MapView = requireNativeComponent('MapView');
...
render() {
return <MapView style={{flex: 1}} />;
}
属性
要桥接原生视图的一些原生属性,我们可以在视图管理器类上声明具有相同名称的属性,并用 @ReactProperty
宏标记它们。假设我们想要能够禁用缩放
@ReactView
class MapView: RCTViewManager {
@ReactProperty
var zoomEnabled: Bool?
override func view() -> UIView {
MKMapView()
}
}
注意 视图的目标属性必须对 Objective-C 可见。
现在要真正禁用缩放,我们在 JavaScript 中设置属性
<MapView style={{flex: 1}} zoomEnabled={false} />
对于更复杂的属性,您可以将 json
从 JavaScript 直接传递到视图的本机属性(如果已实现),或者使用 isCustom
参数来通知 React Native 在您的视图管理器上实现了自定义 setter
@ReactView
class MapView: RCTViewManager {
@ReactProperty
var zoomEnabled: Bool?
@ReactProperty(isCustom: true)
var region: [String : Double]?
@objc
func set_region(_ json: [String : Double]?, forView: MKMapView?, withDefaultView: MKMapView?) {
guard let latitude = json?["latitude"],
let latitudeDelta = json?["latitudeDelta"],
let longitude = json?["longitude"],
let longitudeDelta = json?["longitudeDelta"]
else {
return
}
let region = MKCoordinateRegion(
center: CLLocationCoordinate2D(latitude: latitude, longitude: longitude),
span: MKCoordinateSpan(latitudeDelta: latitudeDelta, longitudeDelta: longitudeDelta)
)
forView?.setRegion(region, animated: true)
}
override func view() -> UIView {
MKMapView()
}
}
注意 自定义 setter 必须具有以下签名:
@objc func set_Name(_ value: Type, forView: ViewType?, withDefaultView: ViewType?)
带有 region
属性的 JavaScript 代码
<MapView
style={{flex: 1}}
zoomEnabled={false}
region={{
latitude: 37.48,
longitude: -122.1,
latitudeDelta: 0.1,
longitudeDelta: 0.1,
}}
/>
事件
要处理来自用户的事件(如更改可见区域),我们可以使用 RCTBubblingEventBlock
类型将 JavaScript 的输入事件处理程序映射到原生视图属性。
让我们向 MKMapView 的子类添加新的 onRegionChange
属性
class NativeMapView: MKMapView {
@objc var onRegionChange: RCTBubblingEventBlock?
}
@ReactView
class MapView: RCTViewManager {
@ReactProperty
var onRegionChange: RCTBubblingEventBlock?
override func view() -> UIView {
let mapView = NativeMapView()
mapView.delegate = self
return mapView
}
}
extension MapView: MKMapViewDelegate {
func mapView(_ mapView: MKMapView, regionDidChangeAnimated animated: Bool) {
guard let mapView = mapView as? NativeMapView else {
return
}
let region = mapView.region
mapView.onRegionChange?([
"latitude": region.center.latitude,
"longitude": region.center.longitude,
"latitudeDelta": region.span.latitudeDelta,
"longitudeDelta": region.span.longitudeDelta,
])
}
}
注意 所有带有
RCTBubblingEventBlock
的属性都必须以on
为前缀,并用@objc
标记。
调用 onRegionChange
事件处理程序属性会导致在 JavaScript 中调用相同的回调属性
function App(): JSX.Element {
...
this.onRegionChange = event => {
const region = event.nativeEvent;
console.log(region.latitude)
};
return (
<MapView
style={{flex: 1}}
onRegionChange={this.onRegionChange}
/>
);
}
有关原生 UI 组件的更多详细信息,请参见:https://reactnative.net.cn/docs/native-components-ios。
该宏导出并注册一个类作为 React Native 的原生模块。
@ReactModule(jsName: String? = nil, requiresMainQueueSetup: Bool = false, methodQueue: DispatchQueue? = nil)
参数
false
,则将在全局队列上调用类初始化器。默认为 false
。该宏将原生模块的方法暴露给 JavaScript。
@ReactMethod(jsName: String? = nil, isSync: Bool = false)
参数
true
,则该方法从 JavaScript 同步在 JavaScript 线程上调用。默认为 false
。注意 如果您选择同步使用方法,您的应用程序将无法再使用 Google Chrome 调试器。这是因为同步方法需要 JS VM 与应用程序共享内存。对于 Google Chrome 调试器,React Native 在 Google Chrome 中的 JS VM 内运行,并通过 WebSockets 与移动设备异步通信。
该宏导出并注册一个类作为 React Native 的原生 UI 组件。
@ReactView(jsName: String? = nil)
参数
该宏将原生视图的属性导出到 JavaScript。
@ReactProperty(keyPath: String? = nil, isCustom: Bool = false)
参数
@objc func set_Name(_ value: Type, forView: ViewType?, withDefaultView: ViewType?)
处理属性。默认为 false
。File > Add Package Dependencies...
。(注意:菜单选项可能因使用的 Xcode 版本而异。)https://github.com/ikhvorost/ReactBridge.git
Dependency Rule
输入一个特定的版本或版本范围,并为 Add to Project
输入所需的目标。import React
import ReactBridge
...
对于 swift 包,您可以直接在您的 Package.swift
文件中将 ReactBridge
添加到您的依赖项中
let package = Package(
...
dependencies: [
.package(url: "https://github.com/ikhvorost/ReactBridge.git", from: "1.0.0")
],
targets: [
.target(name: "YourPackage",
dependencies: [
.product(name: "ReactBridge", package: "ReactBridge")
]
),
...
...
)
本项目根据 MIT 许可证获得许可。有关更多信息,请参见 LICENSE。