iOS 原生与 React Native 混合开发集成文档(新架构)

本文档完整整理了iOS 原生(UIKit/SwiftUI)+ React Native 新架构 桥接、页面跳转、全局初始化、配置项的全套代码与步骤,可直接用于项目集成。

一、集成前提

  1. 已完成 React Native 项目基础搭建,RN 注册模块名为:** MYRN**(可自行修改)

  2. 使用 RN 新架构(Fabric)

  3. 开发环境:Xcode + RN 开发服务器(npm start


二、项目文件结构(本次创建/修改的文件)

  1. ViewController.swift(原生主页面,点击按钮打开 RN)

  2. ReactViewController.swift(RN 容器控制器)

  3. RNPageView.swift(SwiftUI 包装 RN 页面,可选)

  4. AppDelegate.swift(全局 RN 初始化、桥接配置)

  5. Info.plist 网络/架构配置


三、完整代码实现

1. 原生主页面(ViewController.swift)

功能:屏幕中央按钮,点击模态跳转 RN 页面


import UIKit



class ViewController: UIViewController {

    // RN 页面控制器(单例,避免重复创建)

    var reactViewController: ReactViewController?



    override func viewDidLoad() {

        super.viewDidLoad()

        view.backgroundColor = .systemBackground

        

        // 打开 RN 页面按钮

        let openRNButton = UIButton()

        openRNButton.setTitle("打开 React Native", for: .normal)

        openRNButton.setTitleColor(.systemBlue, for: .normal)

        openRNButton.setTitleColor(.blue, for: .highlighted)

        

        // 按钮点击事件

        openRNButton.addAction(UIAction { [weak self] _ in

            guard let self = self else { return }

            // 懒加载创建 RN 控制器,仅初始化一次

            if self.reactViewController == nil {

                self.reactViewController = ReactViewController()

            }

            // 模态弹出 RN 页面

            self.present(self.reactViewController!, animated: true)

        }, for: .touchUpInside)

        

        view.addSubview(openRNButton)

        // 自动布局(居中)

        openRNButton.translatesAutoresizingMaskIntoConstraints = false

        NSLayoutConstraint.activate([

            openRNButton.centerXAnchor.constraint(equalTo: view.centerXAnchor),

            openRNButton.centerYAnchor.constraint(equalTo: view.centerYAnchor)

        ])

    }

}


2. RN 容器控制器(ReactViewController.swift)

功能:纯 UIKit 容器,加载 RN 根视图


import UIKit

import React

import React_RCTAppDelegate

import ReactAppDependencyProvider



class ReactViewController: UIViewController {

    // RN 核心工厂 + 代理

    var reactNativeFactory: RCTReactNativeFactory?

    var reactFactoryDelegate: RCTReactNativeFactoryDelegate?

    

    override func viewDidLoad() {

        super.viewDidLoad()

        setupReactNative()

    }

    

    private func setupReactNative() {

        // 初始化 RN 代理 + 依赖提供者

        reactFactoryDelegate = ReactNativeDelegate()

        reactFactoryDelegate?.dependencyProvider = RCTAppDependencyProvider()

        

        // 创建 RN 工厂

        reactNativeFactory = RCTReactNativeFactory(delegate: reactFactoryDelegate!)

        

        // 加载 RN 注册的模块: MYRN(与 RN 注册名保持一致)

        guard let rnRootView = reactNativeFactory?.rootViewFactory.view(withModuleName: " MYRN") else {

            print("RN 视图加载失败")

            return

        }

        view = rnRootView

    }

}


3. SwiftUI 包装 RN 页面(RNPageView.swift,可选)

功能:SwiftUI 项目中使用 RN 页面,自带加载提示


import SwiftUI

import React

import React_RCTAppDelegate

import ReactAppDependencyProvider



// RN 业务页面(SwiftUI 层)

struct RNPageView: View {

    var body: some View {

        ZStack {

            // 加载占位提示

            Text("RN 加载中...")

                .font(.title)

                .padding()

            

            // 包装 RN 控制器

            RNWrapper()

                .edgesIgnoringSafeArea(.all)

        }

    }

}



// SwiftUI 包装 UIKit 控制器(RN 容器)

struct RNWrapper: UIViewControllerRepresentable {

    func makeUIViewController(context: Context) -> ReactViewController {

        ReactViewController()

    }

    

    func updateUIViewController(_ uiViewController: ReactViewController, context: Context) {

        // 无需更新

    }

}


4. 全局 RN 代理类(直接写在 AppDelegate 内/外部均可)


import React

import React_RCTAppDelegate



class ReactNativeDelegate: RCTDefaultReactNativeFactoryDelegate {

    // RN 包路径配置

    override func sourceURL(for bridge: RCTBridge) -> URL? {

        bundleURL()

    }

    

    override func bundleURL() -> URL? {

        #if DEBUG

        // 开发环境:加载本地 RN 服务器

        RCTBundleURLProvider.sharedSettings().jsBundleURL(forBundleRoot: "index")

        #else

        // 生产环境:加载本地打包文件

        Bundle.main.url(forResource: "main", withExtension: "jsbundle")

        #endif

    }

}


5. AppDelegate.swift(全局初始化核心)

功能:全局唯一初始化 RN 桥接、工厂、启动 RN 引擎


import UIKit

import React

import React_RCTAppDelegate

import ReactAppDependencyProvider



@main

class AppDelegate: UIResponder, UIApplicationDelegate, RCTBridgeDelegate {

    var window: UIWindow?

    

    // RN 全局单例对象(只初始化一次)

    var reactNativeDelegate: ReactNativeDelegate?

    var reactNativeFactory: RCTReactNativeFactory?

    var reactBridge: RCTBridge!

    

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {

        // 1. 初始化 RN 全局配置

        setupGlobalReactNative(launchOptions: launchOptions)

        

        // 2. 设置主窗口(默认加载 ViewController)

        window = UIWindow(frame: UIScreen.main.bounds)

        window?.rootViewController = ViewController()

        window?.makeKeyAndVisible()

        

        return true

    }

    

    // MARK: - 全局初始化 RN

    private func setupGlobalReactNative(launchOptions: [UIApplication.LaunchOptionsKey: Any]?) {

        // 初始化代理 + 工厂

        let rnDelegate = ReactNativeDelegate()

        let rnFactory = RCTReactNativeFactory(delegate: rnDelegate)

        rnDelegate.dependencyProvider = RCTAppDependencyProvider()

        

        // 赋值全局对象

        reactNativeDelegate = rnDelegate

        reactNativeFactory = rnFactory

        

        // 启动 RN 引擎(模块名: MYRN)

        rnFactory.startReactNative(

            withModuleName: " MYRN",

            in: window!,

            launchOptions: launchOptions

        )

        

        // 初始化全局桥接(单例,仅一次)

        reactBridge = RCTBridge(delegate: self, launchOptions: launchOptions)

    }

    

    // MARK: - RCTBridgeDelegate 协议

    func sourceURL(for bridge: RCTBridge) -> URL? {

        #if DEBUG

        return URL(string: "http://localhost:8081/index.bundle?platform=ios")

        #else

        return Bundle.main.url(forResource: "main", withExtension: "jsbundle")

        #endif

    }

}


四、Info.plist 配置(必须添加)

方式:在 Info.plist 中右键 -> Open As -> Source Code,粘贴以下配置:


<key>NSAllowsLocalNetworking</key>

<true/>

<key>NSAllowsArbitraryLoads</key>

<true/>



<!-- RN 新架构启用 -->

<key>RCTNewArchEnabled</key>

<true/>



<!-- 状态栏样式全局控制 -->

<key>UIViewControllerBasedStatusBarAppearance</key>

<false/>


五、关键说明(必看)

  1. RN 模块名统一

    所有 withModuleName: " MYRN" 必须与你 RN 项目中注册的模块名完全一致

  2. 全局单例原则

    • RCTBridgeRCTReactNativeFactory 必须全局初始化一次

    • 禁止在多个页面重复初始化,否则会导致崩溃/内存泄漏

  3. 开发/生产环境区分

    • 调试:加载 localhost:8081 本地包

    • 发布:加载打包后的 main.jsbundle

  4. 页面跳转逻辑

    • 原生页面 → RN 页面:使用 present 模态弹出

    • RN → 原生:需要额外实现桥接方法(本文未包含)


六、运行流程

  1. 启动 RN 开发服务器:npm start

  2. Xcode 运行 iOS 项目

  3. 原生页面点击按钮 → 加载 RN 页面


iOS 模拟器启动与调试模式指定指南

本文整理 查看模拟器、指定模拟器、指定 Debug/Release 模式、组合启动、调试操作、Xcode 操作、常见问题 全套命令与步骤,可直接复制使用。


一、查看所有可用模拟器

用于获取准确模拟器名称 / UDID,避免启动报错

# 列出所有已安装模拟器(名称 + UDID + 运行状态)
xcrun simctl list devices

输出示例

iPhone 15 (XXXX-XXXX-XXXX-XXXX) (Shutdown)
iPhone 15 Pro (YYYY-YYYY-YYYY-YYYY) (Shutdown)
iPad Pro (12.9-inch) (ZZZZ-ZZZZ-ZZZZ-ZZZZ) (Shutdown)

二、指定模拟器启动(3 种写法)

1. 模拟器名称(最常用)

# npm
npm run ios -- --simulator="iPhone 15 Pro"

# yarn
yarn ios --simulator="iPhone 15 Pro"

# npx 直接启动
npx react-native run-ios --simulator="iPhone 15 Pro"

2. UDID(模拟器名称重复时使用)

npm run ios -- --simulator="XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"

3. 名称+系统版本(精确匹配)

npm run ios -- --simulator="iPhone 15 Pro (17.5)"

三、指定 Debug / Release 模式

1. 强制 Debug 模式(开发/热重载/调试)

# 标准写法(推荐)
npm run ios -- --mode=Debug --simulator="iPhone 15 Pro"

# 旧版兼容写法
npm run ios -- --configuration=Debug --simulator="iPhone 15 Pro"

2. 强制 Release 模式(打包测试/性能验证)

npm run ios -- --mode=Release --simulator="iPhone 15 Pro"

四、组合启动(指定模拟器 + Debug + 端口)

最常用完整命令,支持热重载、调试、指定设备

npm run ios -- \
  --simulator="iPhone 15 Pro" \
  --mode=Debug \
  --port=8081

五、启动后开启 JS 远程调试(Debug 必备)

  1. 模拟器中打开 RN 页面
  2. 快捷键:Cmd + D
  3. 选择:Debug with Chrome / Debug with VS Code
  4. 浏览器访问调试地址:http://localhost:8081/debugger-ui/

六、Xcode 中指定模拟器与模式(图形化操作)

  1. 打开 ios/XXX.xcworkspace
  2. 左上角选择项目 Scheme 和目标模拟器
  3. 菜单栏:Product → Scheme → Edit Scheme
  4. Run → Build Configuration 选择 Debug
  5. 点击运行按钮 ▶️ 启动

七、常见问题

  1. 报错:Could not find simulator
    → 模拟器名称错误,用 xcrun simctl list devices 复制准确名称
  2. Release 模式无调试菜单、无法热重载
    → 正常,Release 环境默认禁用开发者功能
  3. 端口 8081 被占用
    → 启动命令添加 --port=8082 更换端口

版本仅供参考:
RN版本:0.85.1
Node版本: v22.22.2

官方文档:https://reactnative.dev/docs/integration-with-existing-apps?language=apple&ios-language=swift

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐