引言
在现代移动应用开发中,React Native (RN)与原生平台的深度集成变得越来越重要。本文基于实际的 iOS 项目代码,详细介绍 RN 与 iOS 之间的通信机制,涵盖模块导出、事件发射、Promise 调用、桥连接等核心概念。
我们现在基于的是React Native的最新版本:v0.82.1。
一、基础架构搭建
1.1 React Native 视图集成
首先需要在 iOS 项目中正确集成 RN 视图。我们使用 RCTReactNativeFactory来创建和管理 RN 组件:
swift
class ReactView: UIView {
var reactNativeFactory: RCTReactNativeFactory?
var reactNativeFactoryDelegate: RCTReactNativeFactoryDelegate?
private func setupView() {
reactNativeFactoryDelegate = ReactNativeDelegate()
reactNativeFactoryDelegate!.dependencyProvider = RCTAppDependencyProvider()
reactNativeFactory = RCTReactNativeFactory(delegate: reactNativeFactoryDelegate
reactNativeFactory!.rootViewFactory.view(withModuleName: "lottieAnimation")
}
}
1.2 调试与发布配置
通过自定义代理类处理不同环境的 bundle 加载:
swift
class ReactNativeDelegate: RCTDefaultReactNativeFactoryDelegate {
override func bundleURL() -> URL? {
#if DEBUG
RCTBundleURLProvider.sharedSettings().jsBundleURL(forBundleRoot: "index")
#else
Bundle.main.url(forResource: "main", withExtension: "jsbundle")
#endif
}
}
二、原生模块导出
2.1 基础模块定义
创建 NativeCommunicationModule类,作为 RN 调用原生功能的主要入口:
swift
@objc(NativeCommunicationModule)
class NativeCommunicationModule: NSObject {
@objc static func moduleName() -> String {
return "NativeCommunicationModule"
}
@objc static func requiresMainQueueSetup() -> Bool {
return true
}
}
2.2 常量导出
向 RN 导出常量信息:
swift
@objc func constantsToExport() -> [String: Any]! {
return [
"moduleVersion": "1.0.0",
"platform": "iOS"
]
}
三、通信机制详解
3.1 Promise 异步通信
实现基于 Promise 的异步方法调用:
swift
@objc
func getIntegrationInfo(_ resolve: @escaping RCTPromiseResolveBlock,
reject: @escaping RCTPromiseRejectBlock) {
let info: [String: Any] = [
"platform": "iOS",
"reactNativeVersion": "0.80+",
"integrationMethod": "RCTReactNativeFactory + EventEmitter",
"timestamp": Date().timeIntervalSince1970
]
resolve(info)
}
RN 端调用方式:
typescript
const { NativeCommunicationModule } = NativeModules;
const sendMessageToNativeWithPromise = async (message: string): Promise<any> => {
return await NativeCommunicationModule.getIntegrationInfo(message);
};
3.2 同步方法调用
对于需要立即返回结果的方法,提供同步接口:
swift
@objc
func getDeviceInfoSync() -> [String: Any] {
return [
"platform": "iOS",
"systemVersion": UIDevice.current.systemVersion,
"model": UIDevice.current.model,
"timestamp": Date().timeIntervalSince1970
]
}
四、事件发射器(Event Emitter)
4.1 事件发射器实现
创建单例事件发射器,支持多种事件类型:
swift
@objc(EventEmitterModule)
class EventEmitterModule: RCTEventEmitter {
@objc static let shared = EventEmitterModule()
override func supportedEvents() -> [String]! {
return [
"onNativeMessage", // 原生消息事件
"onDataUpdate", // 数据更新事件
"onStatusChange", // 状态变化事件
"onCustomEvent", // 自定义事件
"onTimerTick", // 定时器事件
"onNavigation", // 导航事件
"onError" // 错误事件
]
}
}
4.2 事件监听管理
swift
private var hasListeners = false
override func startObserving() {
hasListeners = true
print("✅ JS 开始监听原生事件")
startEventServices()
}
override func stopObserving() {
hasListeners = false
print("🛑 JS 停止监听原生事件")
stopEventServices()
}
4.3 事件发送方法
swift
@objc func sendMessage(_ message: String) {
sendEvent(withName: "onNativeMessage", body: [
"message": message,
"timestamp": Date().timeIntervalSince1970,
"type": "message"
])
}
五、核心桥接机制:连接 Swift 与 JavaScript(重要)
5.1 桥接文件的关键作用
桥接文件是整个通信架构的核心。React Native 的 iOS 端通信基于 Objective-C 运行时,JavaScript 只能通过 Objective-C 的桥接层来调用原生代码。没有这个桥连接文件会导致在RN侧找不到在Native侧定义的模块。
5.2 桥接文件怎么来
当你创建好.m文件或者.mm文件时候,先把所有文件移除引用,然后再添加到工程下面,Xcode会提示你是否创建"yourProject-Bridging-Header",然后选择是就可以了。
5.3桥接文件怎么来
创建好桥接文件,那么现在就可以在.mm或者.m文件中导出swift文件中定义的模块和方法了。
c++
// LottieBridge.mm - 核心桥接文件
#import "lottieAnimation-Bridging-Header.h"
// EventEmitterModule - 事件发射器桥接
@interface RCT_EXTERN_MODULE(EventEmitterModule, RCTEventEmitter)
// 事件发送方法
RCT_EXTERN_METHOD(sendMessage:(NSString *)message eventType:(NSString)eventType)
RCT_EXTERN_METHOD(sendDataUpdate:(NSDictionary *)data)
RCT_EXTERN_METHOD(sendStatusChange:(NSString *)status extraInfo:(NSDictionary *)extraInfo)
RCT_EXTERN_METHOD(sendCustomEvent:(NSString *)eventType data:(NSDictionary *)data)
RCT_EXTERN_METHOD(sendError:(NSString *)errorCode errorMessage:(NSString *)errorMessage details:(NSDictionary *)details)
RCT_EXTERN_METHOD(sendBatchEvents:(NSArray *)events)
// 同步属性访问
RCT_EXTERN__BLOCKING_SYNCHRONOUS_METHOD(isBeingObserved)
@end
// NativeCommunicationModule - 功能模块桥接
@interface RCT_EXTERN_MODULE(NativeCommunicationModule, NSObject)
// Promise 方法
RCT_EXTERN_METHOD(getIntegrationInfo:(NSString *)message resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject)
// 普通方法
RCT_EXTERN_METHOD(sendToNative:(NSString *)message)
@end
做完以上的工作就可以在RN侧使用我们在原生定义的方法了。
六、双向通信实践
6.1 RN 到 iOS 的通信
RN 端发送消息到原生:
typescript
const useSendMessageToNative = () => {
const sendMessageToNative = (message: string): void => {
NativeCommunicationModule.sendToNative(message);
};
return { sendMessageToNative };
};
iOS 端接收并处理消息:
swift
@objc
func sendToNative(_ message: String) {
DispatchQueue.main.async {
print("📱 收到 RN 消息: \(message)")
// 通知原生系统
NotificationCenter.default.post(
name: Notification.Name("ReactNativeMessage"),
object: nil,
userInfo: ["message": message]
)
// 通过 Event Emitter 发送回复
EventEmitterModule.shared.sendMessage("已收到消息: \(message)")
}
}
6.2 iOS 到 RN 的主动通信
原生端主动触发事件:
swift
@objc
func triggerCustomEvent(_ eventType: String, data: [String: Any]?) {
EventEmitterModule.shared.sendCustomEvent(eventType, data: data)
}
RN 端监听事件:
swift
export const useNativeMessageToRN = () => {
useEffect(() => {
const handleNativeMessage = (messageFromNative) => {
const {message, type} = messageFromNative;
console.log('收到原生消息:', message);
// 处理消息...
};
eventEmitter.addListener('onNativeMessage', handleNativeMessage);
return () => eventEmitter.removeAllListeners('onNativeMessage');
}, []);
};
七、内存管理与资源释放
7.1 关键的内存管理问题
在 RN 与 iOS 集成中,内存管理是至关重要的。如果不正确处理,会导致内存泄漏和应用崩溃。
7.2 视图控制器的内存管理
swift
class ViewController: UIViewController {
deinit {
// 关键:在视图控制器销毁时进行清理
ReactNativeViewManager.shared.cleanup()
}
@objc private func cleanupReactNative() {
// 轻量级清理(推荐日常使用)
ReactNativeViewManager.shared.cleanup()
}
@objc private func fullCleanupReactNative() {
// 完整清理(释放所有资源)
ReactNativeViewManager.shared.invalidateReactNative()
}
}
7.3 React Native 视图管理器的内存管理
swift
class ReactNativeViewManager {
static let shared = ReactNativeViewManager()
private var reactNativeFactory: RCTReactNativeFactory?
private var currentRootView: RCTRootView?
// 轻量级清理(保持 Factory 可用)
func cleanup() {
print("执行轻量级 RN 清理")
currentRootView?.removeFromSuperview()
currentRootView = nil
}
// 完整清理(释放所有资源)
func invalidateReactNative() {
print("执行完整 RN 资源释放")
cleanup()
reactNativeFactory = nil
}
deinit {
// 确保资源被正确释放
invalidateReactNative()
}
}
7.4 事件发射器的内存管理
swift
class EventEmitterModule: RCTEventEmitter {
private var timer: Timer?
override func stopObserving() {
hasListeners = false
// 停止定时器和其他服务
stopTimer()
}
private func stopTimer() {
timer?.invalidate()
timer = nil
}
deinit {
// 最终清理
stopTimer()
}
}
7.5 RN 端的内存管理
在 React Native 组件中,也要正确管理事件监听器:
typescript
export const useNativeMessageToRN = () => {
useEffect(() => {
const subscription = eventEmitter.addListener('onNativeMessage', handler);
// 关键:在组件卸载时清理监听器
return () => {
subscription.remove();
};
}, []);
};
7.6 内存管理最佳实践
- 及时清理 :在
deinit和视图消失时释放资源 - 避免循环引用 :在闭包中使用
[weak self] - 分层清理策略:提供轻量级和完整清理两种方式
- 事件监听器管理:确保监听器被正确移除