集成到现有原生应用
如果你正准备从头开始制作一个新的应用,那么 React Native 会是个非常好的选择。但如果你只想给现有的原生应用中添加一两个视图或是业务流程,React Native 也同样不在话下。只需简单几步,你就可以给原有应用加上新的基于 React Native 的特性、画面和视图等。
具体的步骤根据你所开发的目标平台不同而不同。
译注:本文档可能更新不够及时,不能保证适用于最新版本,欢迎了解的朋友使用页面底部的编辑链接帮忙改进此文档。一个实用的建议是可以使用
npx react-native init NewProject
创建一个最新版本的纯 RN 项目,去参考其 Podfile 或是 gradle 等的配置,以它们为准。
- Android (Java & Kotlin)
- iOS (Objective-C and Swift)
核心概念
把 React Native 组件集成到 Android 应用中有如下几个主要步骤:
- 配置好 React Native 依赖和项目结构。
- 创建 js 文件,编写 React Native 组件的 js 代码。
- 在应用中添加一个
ReactRootView
。这个ReactRootView
正是用来承载你的 React Native 组件的容器。 - 启动 React Native 的 Metro 服务,运行应用。
- 验证这部分组件是否正常工作。
开发环境准备
首先按照开发环境搭建教程来安装 React Native 在 Android 平台上所需的一切依赖软件。
1. 配置项目目录结构
首先创建一个空目录用于存放 React Native 项目,然后在其中创建一个/android
子目录,把你现有的 Android 项目拷贝到/android
子目录中。
2. 安装 JavaScript 依赖包
在项目根目录下创建一个名为package.json
的空文本文件,然后填入以下内容:
{
"name": "MyReactNativeApp",
"version": "0.0.1",
"private": true,
"scripts": {
"start": "react-native start"
}
}
示例中的
version
字段没有太大意义(除非你要把你的项目发布到 npm 仓库)。scripts
中是用于启动 Metro 服务的命令。
接下来我们使用 yarn 或 npm(两者都是 node 的包管理器)来安装 React 和 React Native 模块。请打开一个终端/命令提示行,进入到项目目录中(即包含有 package.json 文件的目录),然后运行下列命令来安装:
$ yarn add react-native
这样默认会安装最新版本的 React Native,同时会打印出类似下面的警告信息(你可能需要滚动屏幕才能注意到):
warning "react-native@0.52.2" has unmet peer dependency "react@16.2.0".
这是正常现象,意味着我们还需要安装指定版本的 React:
$ yarn add react@16.2.0
注意必须严格匹配警告信息中所列出的版本,高了或者低了都不可以。
如果你使用多个第三方依赖,可能这些第三方各自要求的 react 版本有所冲突,此时应优先满足
react-native
所需要的react
版本。其他第三方能用则用,不能用则只能考虑选择其他库。
所有 JavaScript 依赖模块都会被安装到项目根目录下的node_modules/
目录中(这个目录我们原则上不复制、不移动、不修改、不上传,随用随装)。
把node_modules/
目录记录到.gitignore
文件中(即不上传到版本控制系统,只保留在本地)。
把 React Native 添加到你的应用中
配置 Gradle
React Native 使用 React Native Gradle Plugin 来配置您的依赖项和项目设置。
首先,让我们通过添加以下行来编辑您的settings.gradle
文件:
includeBuild('../node_modules/@react-native/gradle-plugin')
然后你需要打开顶层的 build.gradle
文件并添加这一行:
buildscript {
repositories {
google()
mavenCentral()
}
dependencies {
classpath("com.android.tools.build:gradle:7.3.1")
+ classpath("com.facebook.react:react-native-gradle-plugin")
}
}
这将确保 React Native Gradle Plugin 在您的项目中可用。
最后,在 app/build.gradle
文件中添加以下行(注意它的路径不同于上面,是app/build.gradle
):
apply plugin: "com.android.application"
+apply plugin: "com.facebook.react"
repositories {
mavenCentral()
}
dependencies {
// Other dependencies here
+ implementation "com.facebook.react:react-android"
+ implementation "com.facebook.react:hermes-android"
}
这些依赖项可在 mavenCentral()
上获得,因此请确保您已在 repositories{}
块中定义它。
我们故意不为这些implementation
依赖项指定版本,因为 React Native Gradle Plugin 会自动处理它。如果您不使用 React Native Gradle Plugin,则必须手动指定版本。
启用原生模块的自动链接
要使用自动链接的功能,我们必须将其应用于几个地方。首先,将以下内容添加到settings.gradle
:
apply from: file("../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesSettingsGradle(settings)
接下来,在app/build.gradle
的最底部添加以下内容:
apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project)
配置权限
接着,在 AndroidManifest.xml
清单文件中声明网络权限:
<uses-permission android:name="android.permission.INTERNET" />
如果需要访问 DevSettingsActivity
界面(即开发者菜单),则还需要在 AndroidManifest.xml
中声明:
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" />
开发者菜单一般仅用于在开发时从 Packager 服务器刷新 JavaScript 代码,所以在正式发布时你可以去掉这一权限。
允许明文传输(http 接口) (API level 28+)
从 Android 9 (API level 28)开始,默认情况下明文传输(http 接口)是禁用的,只能访问 https 接口。这将阻止应用程序连接到Metro bundler。下面的更改允许调试版本中的明文通信。
1. 为 debug 版本启用 usesCleartextTraffic
选项
在src/debug/AndroidManifest.xml
中添加usesCleartextTraffic
选项:
<!-- ... -->
<application
android:usesCleartextTraffic="true" tools:targetApi="28" >
<!-- ... -->
</application>
<!-- ... -->
如果希望在正式打包后也能继续访问 http 接口,则需要在src/main/AndroidManifest.xml
中也添加这一选项。
要了解有关网络安全配置和明文通信策略的更多信息,请参阅此链接。
代码集成
现在我们将修改原生 Android 应用程序以集成 React Native。
React Native 组件
我们首先要写的是"High Score"(得分排行榜)的 JavaScript 端的代码。
1. 创建一个index.js
文件
首先在项目根目录中创建一个空的index.js
文件。(注意一些老的教程可能提到,在 0.49 版本之前是 index.android.js 文件)
index.js
是 React Native 应用在 Android 上的入口文件。而且它是不可或缺的!它可以是个很简单的文件,简单到可以只包含一行require/import
导入语句。本教程中为了简单示范,把全部的代码都写到了index.js
里(当然实际开发中我们并不推荐这样做)。
2. 添加你自己的 React Native 代码
在index.js
中添加你自己的组件。这里我们只是简单的添加一个<Text>
组件,然后用一个带有样式的<View>
组件把它包起来。
import React from 'react';
import {AppRegistry, StyleSheet, Text, View} from 'react-native';
const HelloWorld = () => {
return (
<View style={styles.container}>
<Text style={styles.hello}>Hello, World</Text>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
},
hello: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
});
AppRegistry.registerComponent(
'MyReactNativeApp',
() => HelloWorld,
);
3. 配置权限以便开发中的红屏错误能正确显示
如果你的应用会运行在 Android 6.0(API level 23)或更高版本,请确保你在开发版本中有打开悬浮窗(overlay)
权限。你可以在代码中使用Settings.canDrawOverlays(this);
来检查。之所以需要这一权限,是因为我们会把开发中的报错显示在悬浮窗中(仅在开发阶段需要)。在 Android 6.0(API level 23)中用户需要手动同意授权。具体请求授权的做法是在onCreate()
中添加如下代码。其中OVERLAY_PERMISSION_REQ_CODE
是用于回传授权结果的字段。
companion object {
const val OVERLAY_PERMISSION_REQ_CODE = 1 // 任写一个值
}
...
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if(!Settings.canDrawOverlays(this)) {
val intent = Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
Uri.parse("package: $packageName"))
startActivityForResult(intent, OVERLAY_PERMISSION_REQ_CODE);
}
}
最后,必须重写onActivityResult()
方法(如下面的代码所示)来处理权限接受或拒绝情况以实现一致的用户体验。此外,为了集成使用 startActivityForResult 的原生模块,我们需要将结果传递给 ReactInstanceManager 实例的 onActivityResult 方法。
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (requestCode == OVERLAY_PERMISSION_REQ_CODE) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (!Settings.canDrawOverlays(this)) {
// SYSTEM_ALERT_WINDOW permission not granted
}
}
}
reactInstanceManager?.onActivityResult(this, requestCode, resultCode, data)
}
核心组件:ReactRootView
我们还需要添加一些原生代码来启动 React Native 的运行时环境并让它开始渲染。首先需要在一个Activity
中创建一个ReactRootView
对象,然后在这个对象之中启动 React Native 应用,并将它设为界面的主视图。
如果你要在安卓 5.0 以下的系统上运行,请用
com.android.support:appcompat
包中的AppCompatActivity
代替Activity
。
class MyReactActivity : Activity(), DefaultHardwareBackBtnHandler {
private lateinit var reactRootView: ReactRootView
private lateinit var reactInstanceManager: ReactInstanceManager
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
SoLoader.init(this, false)
reactRootView = ReactRootView(this)
val packages: List<ReactPackage> = PackageList(application).packages
// 有一些第三方可能不能自动链接,对于这些包我们可以用下面的方式手动添加进来:
// packages.add(MyReactNativePackage())
// 同时需要手动把他们添加到`settings.gradle`和 `app/build.gradle`配置文件中。
reactInstanceManager = ReactInstanceManager.builder()
.setApplication(application)
.setCurrentActivity(this)
.setBundleAssetName("index.android.bundle")
.setJSMainModulePath("index")
.addPackages(packages)
.setUseDeveloperSupport(BuildConfig.DEBUG)
.setInitialLifecycleState(LifecycleState.RESUMED)
.build()
// 注意这里的 MyReactNativeApp 必须对应"index.js"中的
// "AppRegistry.registerComponent()"的第一个参数
reactRootView?.startReactApplication(reactInstanceManager, "MyReactNativeApp", null)
setContentView(reactRootView)
}
override fun invokeDefaultOnBackPressed() {
super.onBackPressed()
}
}
执行"Sync Project files with Gradle"操作。
如果你使用的是 Android Studio , 可以使用Alt + Enter
快捷键来自动为 MyReactActivity 类补上缺失的 import 语句。注意BuildConfig
应该是在你自己的包中自动生成,无需额外引入。千万不要从com.facebook...
的包中引入!
我们需要把 MyReactActivity
的主题设定为 Theme.AppCompat.Light.NoActionBar
,因为里面有许多组件都使用了这一主题。
<activity
android:name=".MyReactActivity"
android:label="@string/app_name"
android:theme="@style/Theme.AppCompat.Light.NoActionBar">
</activity>
一个
ReactInstanceManager
可以在多个 activities 或 fragments 间共享。你将需要创建自己的ReactFragment
或ReactActivity
,并拥有一个保存ReactInstanceManager
的单例持有者。当你需要ReactInstanceManager
(例如,将ReactInstanceManager
连接到这些 Activities 或 Fragments 的生命周期)时,请使用单例提供的那个。
下一步我们需要把一些 activity 的生命周期回调传递给ReactInstanceManager
:
override fun onPause() {
super.onPause()
reactInstanceManager.onHostPause(this)
}
override fun onResume() {
super.onResume()
reactInstanceManager.onHostResume(this, this)
}
override fun onDestroy() {
super.onDestroy()
reactInstanceManager.onHostDestroy(this)
reactRootView.unmountReactApplication()
}
我们还需要把后退按钮事件传递给 React Native:
override fun onBackPressed() {
reactInstanceManager.onBackPressed()
super.onBackPressed()
}
这允许 JavaScript 控制用户按下设备后退按钮时发生的情况(例如,执行导航时)。当 JavaScript 不处理后退按钮按下的情况时,将调用invokeDefaultOnBackPressed
方法。默认情况下,这将完成你的Activity
。
最后,我们需要连接开发菜单。默认情况下通过(狂)摇晃设备来激活,但这在模拟器中不是很有用,只有当你按下设备菜单按钮时才显示(如果你使用的是 Android Studio 模拟器,请使用Ctrl + M):
override fun onKeyUp(keyCode: Int, event: KeyEvent?): Boolean {
if (keyCode == KeyEvent.KEYCODE_MENU && reactInstanceManager != null) {
reactInstanceManager.showDevOptionsDialog()
return true
}
return super.onKeyUp(keyCode, event)
}
现在 activity 已就绪,可以运行一些 JavaScript 代码了。
测试集成结果
你已经完成了将 React Native 与当前应用程序集成的所有基本步骤。现在我们将启动Metro bundler来构建index.bundle
包,并通过本地主机提供服务。
1. 运行 Metro 服务
运行应用首先需要启动开发服务器(Metro)。你只需在项目根目录中执行以下命令即可:
$ yarn start
2. 运行你的应用
保持 Metro 的窗口运行不要关闭,然后像往常一样编译运行你的 Android 应用(在命令行中执行./gradlew installDebug
或是在 Android Studio 中编译运行)。
编译执行一切顺利进行之后,在进入到 MyReactActivity 时应该就能立刻从 Metro 中读取 JavaScript 代码并执行和显示:
在 Android Studio 中打包
你也可以使用 Android Studio 来打 release 包!其步骤基本和原生应用一样,只是如果你没有使用 React Native Gradle Plugin 的话,则在每次编译打包之前需要先执行 js 文件的打包(即生成离线的 jsbundle 文件)。具体的 js 打包命令如下:
# 注:如果你使用了 React Native Gradle Plugin,则其会自动执行以下命令,不需要手动执行
$ npx react-native bundle --platform android --dev false --entry-file index.js --bundle-output android/com/your-company-name/app-package-name/src/main/assets/index.android.bundle --assets-dest android/com/your-company-name/app-package-name/src/main/res/
注意把上述命令中的路径替换为你实际项目的路径。如果 assets 目录不存在,则需要提前自己创建一个。
然后在 Android Studio 中正常生成 release 版本即可!
然后呢?
然后就可以开发啦~可是我完全不会 React Native 怎么办?
我们建议你先通读本站的所有文档,看看博客,看看论坛。如果觉得知识太零散,不够系统,那么你也可以考虑下购买我们的付费咨询服务。
关键概念
将 React Native 组件集成到 iOS 应用程序中的关键步骤是:
- 设置正确的目录结构。
- 安装必要的 NPM 依赖项。
- 在 Podfile 配置中添加 React Native。
- 为你的第一个 React Native 屏幕编写 TypeScript 代码。
- 使用
RCTRootView
将 React Native 与你的 iOS 代码集成。 - 通过运行打包器并查看应用程序运行情况来测试你的集成。
使用社区模板
在您遵循本指南时,我们建议您参考React Native 社区模板。该模板包含一个最小化的 iOS 应用,将帮助您理解如何将 React Native 集成到现有的 iOS 应用中。
先决条件
请按照设置开发环境指南指南来配置您的开发环境,以便构建 iOS 平台的 React Native 应用。
本指南还假设您熟悉 iOS 开发的基础知识,如创建UIViewController
和编辑Podfile
文件。
1. 设置目录结构
为确保顺利体验,请为您的集成 React Native 项目创建一个新文件夹,然后将现有的 iOS 项目移动到 /ios
子文件夹中。
2. 安装 NPM 依赖
进入根目录并运行以下命令:
curl -O https://raw.githubusercontent.com/react-native-community/template/refs/heads/0.76-stable/template/package.json
这将从社区模板 复制 package.json
文件到您的项目中。
接下来,运行以下命令安装 NPM 包:
- npm
- Yarn
npm install
yarn install
安装过程创建了一个新的 node_modules
文件夹。该文件夹存储了构建项目所需的 JavaScript 依赖项。
将 node_modules/
添加到您的 .gitignore
文件中(社区默认文件)。
3. 安装开发工具
Xcode 命令行工具
安装命令行工具。选择 Xcode 菜单中的 Settings...(或 Preferences...)。转到位置面板,并安装工具,方法是选择命令行工具下拉菜单中最新的版本。
CocoaPods
CocoaPods 是 iOS 和 macOS 开发的包管理工具。我们使用它将实际的 React Native 框架代码添加到您的当前项目中。
我们建议使用 Homebrew 安装 CocoaPods:
brew install cocoapods
4. 将 React Native 添加到您的应用
配置 CocoaPods
要配置 CocoaPods,我们需要两个文件:
- 一个 Gemfile 文件,定义了我们需要的 Ruby 依赖项。
- 一个 Podfile 文件,定义了如何正确安装我们的依赖项。
对于 Gemfile,请进入您的项目根目录并运行以下命令:
curl -O https://raw.githubusercontent.com/react-native-community/template/refs/heads/0.76-stable/template/Gemfile
这将下载 Gemfile 文件。
对于 Podfile,请进入您的项目 ios
文件夹并运行以下命令:
curl -O https://raw.githubusercontent.com/react-native-community/template/refs/heads/0.76-stable/template/ios/Podfile
请使用社区模板 作为 Gemfile 和 Podfile 的参考。
现在,我们需要运行一些额外的命令来安装 Ruby Gem 和 Pods。
进入 ios
文件夹并运行以下命令:
bundle install
bundle exec pod install
第一个命令将安装 Ruby 依赖项,第二个命令将实际将 React Native 代码集成到您的应用程序中,以便您的 iOS 文件可以导入 React Native 头文件。
5. 编写 TypeScript 代码
现在我们将修改原生 iOS 应用程序以集成 React Native。
我们将编写的第一个代码片段是实际的 React Native 代码,该代码将集成到我们的应用程序中。
创建一个 index.js
文件
首先,在 React Native 项目的根目录中创建一个空的 index.js
文件。
index.js
是 React Native 应用程序的起点,并且总是需要。它可以是一个小文件,该文件 import
其他文件,这些文件是您的 React Native 组件或应用程序的一部分,或者它可以包含所有需要的代码。
我们的 index.js
文件应如下所示(社区模板文件 作为参考):
import {AppRegistry} from 'react-native';
import App from './App';
AppRegistry.registerComponent('HelloWorld', () => App);
创建一个 App.tsx
文件
让我们创建一个 App.tsx
文件。这是一个 TypeScript 文件,可以包含 JSX 表达式。它包含我们要集成到 iOS 应用程序中的根 React Native 组件(链接):
import React from 'react';
import {
SafeAreaView,
ScrollView,
StatusBar,
StyleSheet,
Text,
useColorScheme,
View,
} from 'react-native';
import {
Colors,
DebugInstructions,
Header,
ReloadInstructions,
} from 'react-native/Libraries/NewAppScreen';
function App(): React.JSX.Element {
const isDarkMode = useColorScheme() === 'dark';
const backgroundStyle = {
backgroundColor: isDarkMode ? Colors.darker : Colors.lighter,
};
return (
<SafeAreaView style={backgroundStyle}>
<StatusBar
barStyle={isDarkMode ? 'light-content' : 'dark-content'}
backgroundColor={backgroundStyle.backgroundColor}
/>
<ScrollView
contentInsetAdjustmentBehavior="automatic"
style={backgroundStyle}>
<Header />
<View
style={{
backgroundColor: isDarkMode
? Colors.black
: Colors.white,
padding: 24,
}}>
<Text style={styles.title}>Step One</Text>
<Text>
Edit <Text style={styles.bold}>App.tsx</Text> to
change this screen and see your edits.
</Text>
<Text style={styles.title}>See your changes</Text>
<ReloadInstructions />
<Text style={styles.title}>Debug</Text>
<DebugInstructions />
</View>
</ScrollView>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
title: {
fontSize: 24,
fontWeight: '600',
},
bold: {
fontWeight: '700',
},
});
export default App;
社区模板文件 作为参考
5. 与 iOS 代码集成
我们现在需要添加一些原生代码,以便启动 React Native 运行时并告诉它渲染我们的 React 组件。
准备工作
React Native 应该在 AppDelegate
中调用。假设您的 AppDelegate
如下所示:
- ObjectiveC
- Swift
#import "AppDelegate.h"
#import "ViewController.h"
@interface AppDelegate ()
@end
@implementation AppDelegate {
UIWindow *window;
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
window = [UIWindow new];
window.rootViewController = [ViewController new];
[window makeKeyAndVisible];
return YES;
}
@end
import UIKit
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// 在应用程序启动后进行自定义。
window = UIWindow()
window?.rootViewController = ViewController()
window?.makeKeyAndVisible()
return true
}
}
修改 AppDelegate
类
首先,我们需要扩展 AppDelegate
以继承 React Native 提供的一个类:RCTAppDelegate
。
- ObjectiveC
- Swift
要实现这一点,我们需要修改 AppDelegate.h
和 AppDelegate.m
文件:
- 打开
AppDelegate.h
文件并修改如下(参考社区模板文件):
#import <UIKit/UIKit.h>
+#import <React-RCTAppDelegate/RCTAppDelegate.h>
-@interface AppDelegate : UIResponder <UIApplicationDelegate>
+@interface AppDelegate : RCTAppDelegate
@end
- 打开
AppDelegate.mm
文件并修改如下(参考社区模板文件):
#import "AppDelegate.h"
#import "ViewController.h"
+#import <React/RCTBundleURLProvider.h>
@interface AppDelegate ()
@end
@implementation AppDelegate {
UIWindow *window;
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
+ self.automaticallyLoadReactNativeWindow = NO;
+ return [super application:application didFinishLaunchingWithOptions:launchOptions];
window = [UIWindow new];
window.rootViewController = [ViewController new];
[window makeKeyAndVisible];
return YES;
}
+- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
+{
+ return [self bundleURL];
+}
+- (NSURL *)bundleURL
+{
+#if DEBUG
+ return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"];
+#else
+ return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
+#endif
+}
@end
让我们看看上面的代码:
- 我们继承自
RCTAppDelegate
并调用RCTAppDelegate
的application:didFinishLaunchingWithOptions
方法。这会将所有 React Native 初始化过程委托给基类。 - 我们通过将
automaticallyLoadReactNativeWindow
设置为NO
来定制RCTAppDelegate
。这一步告诉 React Native 应用程序正在处理UIWindow
,React Native 不应担心这一点。 - 方法
sourceURLForBridge:
和bundleURL
用于告诉 React Native 在哪里可以找到需要渲染的 JS 包。sourceURLForBridge:
是旧架构,您可以看到它将决定权委托给了bundleURL
方法,该方法由新架构要求。
要实现这一点,我们需要修改 AppDelegate.swift
文件:
- 打开
AppDelegate.swift
文件并修改如下(参考社区模板文件):
import UIKit
+import React_RCTAppDelegate
+import React_RCTAppDelegate
@main
-class AppDelegate: UIResponder, UIApplicationDelegate {
+class AppDelegate: RCTAppDelegate {
- var window: UIWindow?
- func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
+ override func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
+ self.automaticallyLoadReactNativeWindow = false
+ super.application(application, didFinishLaunchingWithOptions: launchOptions)
window = UIWindow()
- window?.rootViewController = ViewController()
- window?.makeKeyAndVisible()
+ window.rootViewController = ViewController()
+ window.makeKeyAndVisible()
return true
}
+ override func sourceURL(for bridge: RCTBridge) -> URL? {
+ self.bundleURL()
+ }
+ override func bundleURL() -> URL? {
+#if DEBUG
+ RCTBundleURLProvider.sharedSettings().jsBundleURL(forBundleRoot: "index")
+#else
+ Bundle.main.url(forResource: "main", withExtension: "jsbundle")
+#endif
+ }
}
让我们看看上面的代码:
- 我们继承自
RCTAppDelegate
并调用RCTAppDelegate
的application(_:didFinishLaunchingWithOptions:)
方法。这会将所有 React Native 初始化过程委托给基类。 - 我们通过将
automaticallyLoadReactNativeWindow
设置为false
来定制RCTAppDelegate
。这一步告诉 React Native 应用程序正在处理UIWindow
,React Native 不应担心这一点。 - 方法
sourceURLForBridge(for:)
和bundleURL()
用于告诉 React Native 在哪里可以找到需要渲染的 JS 包。sourceURLForBridge(for:)
是旧架构,您可以看到它将决定权委托给了bundleURL()
方法,该方法由新架构要求。
在 rootViewController 中展示 React Native 视图
最后,我们可以展示我们的 React Native 视图。为此,我们需要一个可以承载视图的新视图控制器,我们可以在其中加载 JS 内容。
- 从 Xcode 中,创建一个新
UIViewController
(我们称之为ReactViewController
)。 - 让初始
ViewController
展示ReactViewController
。有几种方法可以做到这一点,具体取决于您的应用程序。对于此示例,我们假设您有一个按钮,用于模态展示 React Native。
- ObjectiveC
- Swift
#import "ViewController.h"
+#import "ReactViewController.h"
@interface ViewController ()
@end
- @implementation ViewController
+@implementation ViewController {
+ ReactViewController *reactViewController;
+}
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
self.view.backgroundColor = UIColor.systemBackgroundColor;
+ UIButton *button = [UIButton new];
+ [button setTitle:@"Open React Native" forState:UIControlStateNormal];
+ [button setTitleColor:UIColor.systemBlueColor forState:UIControlStateNormal];
+ [button setTitleColor:UIColor.blueColor forState:UIControlStateHighlighted];
+ [button addTarget:self action:@selector(presentReactNative) forControlEvents:UIControlEventTouchUpInside];
+ [self.view addSubview:button];
+ button.translatesAutoresizingMaskIntoConstraints = NO;
+ [NSLayoutConstraint activateConstraints:@[
+ [button.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor],
+ [button.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor],
+ [button.centerYAnchor constraintEqualToAnchor:self.view.centerYAnchor],
+ [button.centerXAnchor constraintEqualToAnchor:self.view.centerXAnchor],
+ ]];
}
+- (void)presentReactNative
+{
+ if (reactViewController == NULL) {
+ reactViewController = [ReactViewController new];
+ }
+ [self presentViewController:reactViewController animated:YES completion:nil];
+}
@end
import UIKit
class ViewController: UIViewController {
+ var reactViewController: ReactViewController?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
self.view.backgroundColor = .systemBackground
+ let button = UIButton()
+ button.setTitle("Open React Native", for: .normal)
+ button.setTitleColor(.systemBlue, for: .normal)
+ button.setTitleColor(.blue, for: .highlighted)
+ button.addAction(UIAction { [weak self] _ in
+ guard let self else { return }
+ if reactViewController == nil {
+ reactViewController = ReactViewController()
+ }
+ present(reactViewController!, animated: true)
+ }, for: .touchUpInside)
+ self.view.addSubview(button)
+
+ button.translatesAutoresizingMaskIntoConstraints = false
+ NSLayoutConstraint.activate([
+ button.leadingAnchor.constraint(equalTo: self.view.leadingAnchor),
+ button.trailingAnchor.constraint(equalTo: self.view.trailingAnchor),
+ button.centerXAnchor.constraint(equalTo: self.view.centerXAnchor),
+ button.centerYAnchor.constraint(equalTo: self.view.centerYAnchor),
+ ])
}
}
- 更新
ReactViewController
代码如下:
- ObjectiveC
- Swift
#import "ReactViewController.h"
+#import <React-RCTAppDelegate/RCTRootViewFactory.h>
+#import <React-RCTAppDelegate/RCTAppDelegate.h>
@interface ReactViewController ()
@end
@implementation ReactViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
+ RCTRootViewFactory *factory = ((RCTAppDelegate *)RCTSharedApplication().delegate).rootViewFactory;
+ self.view = [factory viewWithModuleName:@"HelloWorld"];
}
@end
import UIKit
+import React_RCTAppDelegate
class ReactViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
+ let factory = (RCTSharedApplication()?.delegate as? RCTAppDelegate)?.rootViewFactory
+ self.view = factory?.view(withModuleName: "HelloWorld")
}
}
- 确保禁用沙盒脚本。为此,在 Xcode 中,点击您的应用,然后点击构建设置。过滤脚本并设置
User Script Sandboxing
为NO
。这一步是为了在调试和发布版本之间正确切换 Hermes 引擎。
;
6. 测试您的集成
您已经完成了将 React Native 与您的应用程序集成所需的所有基本步骤。现在我们将启动 Metro bundler 来构建您的 TypeScript 应用程序代码。Metro 的 HTTP 服务器从您的开发环境共享 bundle 到模拟器或设备。这允许 热重载。
首先,您需要在项目根目录中创建一个 metro.config.js
文件,如下所示:
const {getDefaultConfig} = require('@react-native/metro-config');
module.exports = getDefaultConfig(__dirname);
您可以查看社区模板文件 作为参考。
一旦您有了配置文件,您可以运行 bundler。在项目根目录下运行以下命令:
- npm
- Yarn
npm start
yarn start
现在,像往常一样构建和运行您的 iOS 应用。
一旦您到达您的 React 驱动的 Activity 中,它应该从开发服务器加载 JavaScript 代码并显示:

在 Xcode 中创建发布版本
您可以使用 Xcode 创建您的发布版本!唯一的额外步骤是添加一个脚本,当应用程序构建时,将您的 JS 和图像打包到 iOS 应用程序中。
- 在 Xcode 中,选择您的应用
- 点击
Build Phases
- 点击左上角的
+
并选择New Run Script Phase
- 点击
Run Script
行并重命名脚本为Bundle React Native code and images
- 在文本框中粘贴以下脚本
set -e
WITH_ENVIRONMENT="$REACT_NATIVE_PATH/scripts/xcode/with-environment.sh"
REACT_NATIVE_XCODE="$REACT_NATIVE_PATH/scripts/react-native-xcode.sh"
/bin/sh -c "$WITH_ENVIRONMENT $REACT_NATIVE_XCODE"
- 将脚本拖放到名为
[CP] Embed Pods Frameworks
的脚本之前。
现在,如果您为发布版本构建您的应用,它将按预期工作。
现在呢?
此时,您可以继续像往常一样开发您的应用。请参阅我们的调试和部署文档,了解更多关于使用 React Native 的信息。