前言
本文主要介紹如何將ReactNative集成到原生的iOS項(xiàng)目當(dāng)中, 并在iOS的原生項(xiàng)目當(dāng)中去調(diào)用使用RN組件,把 ReactNative組件集成到iOS應(yīng)用中有如下幾個主要步驟:
- 配置好 React Native 依賴和項(xiàng)目結(jié)構(gòu)。
- 了解你要集成的 React Native 組件。
- 使用 CocoaPods 把這些組件以依賴的形式加入到項(xiàng)目中。
- 創(chuàng)建 js 文件,編寫 React Native 組件的 js 代碼。
- 在應(yīng)用中添加一個RCTRootView。這個RCTRootView正是用來承載你的 React Native 組件的容器。
- 啟動 React Native 的 Packager 服務(wù),運(yùn)行應(yīng)用。
- 驗(yàn)證這部分組件是否正常工作。
1. 創(chuàng)建iOS原生項(xiàng)目
創(chuàng)建一個名為iOSDemo的iOS原生App項(xiàng)目, 并在項(xiàng)目根目錄下創(chuàng)建一個名為ReactNative的空目錄文件,用于存放和RN相關(guān)的文件, 項(xiàng)目目錄結(jié)構(gòu)如下

2. 創(chuàng)建React-Native項(xiàng)目
創(chuàng)建一個名為rndemo的RN項(xiàng)目,并保證可以順利的運(yùn)行起來, 如何搭建RN環(huán)境和創(chuàng)建RN項(xiàng)目這里不做過多說明,網(wǎng)上有很多相關(guān)文章, 也可以看我之前的相關(guān)環(huán)境搭建文章, RN項(xiàng)目創(chuàng)建完成后,會自動創(chuàng)建相關(guān)的支持文件以及目錄,代碼結(jié)構(gòu)如下:

RN運(yùn)行起來效果如下:

一會我們就會將該頁面集成到iOS的項(xiàng)目當(dāng)中去.并在iOS端調(diào)用打開.
3. 安裝 JavaScript 依賴包
在iOS項(xiàng)目的ReactNative文件夾中創(chuàng)建一個名為package.json的空文本文件

在package.json文件中填入以下內(nèi)容:
{
"name": "mydemo",
"version": "0.0.1",
"private": true,
"scripts": {
"start": "yarn react-native start"
},
"dependencies": {
"react": "16.9.0",
"react-native": "0.61.5"
}
}
注意: react 和 react-native的版本號需要和RN項(xiàng)目中的版本對應(yīng), 可以參考rndemo中的package.json文件.
version字段沒有太大意義(除非你要把你的項(xiàng)目發(fā)布到 npm 倉庫)。scripts中是用于啟動 packager 服務(wù)的命令。
接下來我們使用 yarn 或 npm(兩者都是 node 的包管理器)來安裝JavaScript所依賴模塊(其實(shí)就是React和ReactNative框架)。請打開一個終端/命令提示行,進(jìn)入到iOS項(xiàng)目的ReactNative目錄中(即包含有 package.json 文件的目錄),然后運(yùn)行下列命令來安裝:
$ npm install
執(zhí)行完成以后, 所有 JavaScript 依賴模塊都會被安裝到項(xiàng)目根目錄下的node_modules/目錄中(這個目錄我們原則上不復(fù)制、不移動、不修改、不上傳,隨用隨裝)。
把node_modules/目錄記錄到.gitignore文件中(即不上傳到版本控制系統(tǒng),只保留在本地)。 node_modules/和iOS的'Pods'文件類似
安裝完成后, 目錄如下:

4. CocoaPods集成
通過CocoaPods 把
ReactNative依賴的環(huán)境集成到你當(dāng)前的項(xiàng)目中。
在iOS項(xiàng)目中,創(chuàng)建Podfile文件,如何創(chuàng)建大家都懂, 打開Podfile文件,修改如下:
# Uncomment the next line to define a global platform for your project
platform :ios, '9.0'
target 'iOSDemo' do
# Comment the next line if you don't want to use dynamic frameworks
use_frameworks!
pod 'FBLazyVector', :path => "./ReactNative/node_modules/react-native/Libraries/FBLazyVector"
pod 'FBReactNativeSpec', :path => "./ReactNative/node_modules/react-native/Libraries/FBReactNativeSpec"
pod 'RCTRequired', :path => "./ReactNative/node_modules/react-native/Libraries/RCTRequired"
pod 'RCTTypeSafety', :path => "./ReactNative/node_modules/react-native/Libraries/TypeSafety"
pod 'React', :path => './ReactNative/node_modules/react-native/'
pod 'React-Core', :path => './ReactNative/node_modules/react-native/'
pod 'React-CoreModules', :path => './ReactNative/node_modules/react-native/React/CoreModules'
pod 'React-Core/DevSupport', :path => './ReactNative/node_modules/react-native/'
pod 'React-RCTActionSheet', :path => './ReactNative/node_modules/react-native/Libraries/ActionSheetIOS'
pod 'React-RCTAnimation', :path => './ReactNative/node_modules/react-native/Libraries/NativeAnimation'
pod 'React-RCTBlob', :path => './ReactNative/node_modules/react-native/Libraries/Blob'
pod 'React-RCTImage', :path => './ReactNative/node_modules/react-native/Libraries/Image'
pod 'React-RCTLinking', :path => './ReactNative/node_modules/react-native/Libraries/LinkingIOS'
pod 'React-RCTNetwork', :path => './ReactNative/node_modules/react-native/Libraries/Network'
pod 'React-RCTSettings', :path => './ReactNative/node_modules/react-native/Libraries/Settings'
pod 'React-RCTText', :path => './ReactNative/node_modules/react-native/Libraries/Text'
pod 'React-RCTVibration', :path => './ReactNative/node_modules/react-native/Libraries/Vibration'
pod 'React-Core/RCTWebSocket', :path => './ReactNative/node_modules/react-native/'
pod 'React-cxxreact', :path => './ReactNative/node_modules/react-native/ReactCommon/cxxreact'
pod 'React-jsi', :path => './ReactNative/node_modules/react-native/ReactCommon/jsi'
pod 'React-jsiexecutor', :path => './ReactNative/node_modules/react-native/ReactCommon/jsiexecutor'
pod 'React-jsinspector', :path => './ReactNative/node_modules/react-native/ReactCommon/jsinspector'
pod 'ReactCommon/jscallinvoker', :path => "./ReactNative/node_modules/react-native/ReactCommon"
pod 'ReactCommon/turbomodule/core', :path => "./ReactNative/node_modules/react-native/ReactCommon"
pod 'Yoga', :path => './ReactNative/node_modules/react-native/ReactCommon/yoga'
pod 'DoubleConversion', :podspec => './ReactNative/node_modules/react-native/third-party-podspecs/DoubleConversion.podspec'
pod 'glog', :podspec => './ReactNative/node_modules/react-native/third-party-podspecs/glog.podspec'
pod 'Folly', :podspec => './ReactNative/node_modules/react-native/third-party-podspecs/Folly.podspec'
end
注意: path => './ReactNative/node_modules/ 中的路徑需要和自己實(shí)際項(xiàng)目的JS依賴文件目錄對應(yīng)
提示,可以參考rndemo項(xiàng)目中ios部分的Podfile文件, 位置如下, 將里面依賴項(xiàng)拷貝到iOS項(xiàng)目當(dāng)中,但需要修改路徑

Podfile文件修改完成后, 就可以開始安裝 React Native 的 pod 包了, 在終端執(zhí)行
$ pod install

看到此界面,表示集成成功.
5. 代碼集成
上述我們已經(jīng)準(zhǔn)備好了所有依賴,可以開始在原生iOS應(yīng)用中把 React Native代碼 真正集成進(jìn)來了, 接下來我們就通過將示例rndemo頁面集成到到iOSDemo中來
React Native 組件
1. 創(chuàng)建一個index.js文件
我們在iOS的ReactNative中創(chuàng)建一個空的index.js文件。(注意在 0.49 版本之前是 index.ios.js 文件)

index.js是React Native應(yīng)用在 iOS 上的入口文件。而且它是不可或缺的!
在新建的index.js中寫入以下代碼:
import {AppRegistry} from 'react-native';
import App from './App';
AppRegistry.registerComponent("rndemo", () => App);
rndemo是整體 js 模塊(即你所有的 js 代碼)的名稱。你在 iOS 原生代碼中添加 React Native 視圖時會用到這個名稱。
2. 添加你自己的 React Native 代碼
將我們需要使用的RN頁面代碼導(dǎo)入到iOS項(xiàng)目中, rndemo項(xiàng)目中的App.js文件復(fù)制到和index.js同目錄下,如下圖

App.js即是我們在步驟二看到的RN歡迎頁代碼
3.掌握核心科技: RCTRootView
現(xiàn)在我們已經(jīng)在index.js中創(chuàng)建了 React Native 組件,下一步就是把這個組件添加給一個新的或已有的ViewController。
1.創(chuàng)建一個按鈕,用于跳轉(zhuǎn)RN頁面,界面如下

2. 創(chuàng)建RN視圖:
首先導(dǎo)入RCTRootView的頭文件。
#import <React/RCTRootView.h>
在iOS項(xiàng)目中創(chuàng)建一個按鈕用于跳轉(zhuǎn)RN頁面,代碼如下:
- (IBAction)RNPageButtonDidClick:(UIButton *)sender {
NSURL *jsCodeLocation = [NSURL URLWithString:@"http://localhost:8081/index.bundle?platform=ios"];
RCTRootView *rootView =
[[RCTRootView alloc] initWithBundleURL: jsCodeLocation
moduleName: @"rndemo"
initialProperties: nil
launchOptions: nil];
UIViewController *vc = [[UIViewController alloc] init];
vc.view = rootView;
[self.navigationController pushViewController:vc animated:YES];
}
6. 測試集成結(jié)果
1. 添加 App Transport Security 例外
Apple 現(xiàn)在默認(rèn)會阻止讀取不安全的 HTTP 鏈接。所以我們需要把本地運(yùn)行的 Packager 服務(wù)添加到Info.plist的例外中,以便能正常訪問 Packager 服務(wù):
<key>NSAppTransportSecurity</key>
<dict>
<key>NSExceptionDomains</key>
<dict>
<key>localhost</key>
<dict>
<key>NSTemporaryExceptionAllowsInsecureHTTPLoads</key>
<true/>
</dict>
</dict>
</dict>
2. 運(yùn)行 Packager
要運(yùn)行應(yīng)用,首先需要啟動開發(fā)服務(wù)器(即 Packager,它負(fù)責(zé)實(shí)時監(jiān)測 js 文件的變動并實(shí)時打包,輸出給客戶端運(yùn)行)。具體步驟,進(jìn)入iOS項(xiàng)目的index.js所在目錄:終端運(yùn)行:
$ npm start
3. 運(yùn)行應(yīng)用
如果你使用的是 Xcode,那么照常編譯和運(yùn)行應(yīng)用即可。如果你沒有使用 Xcode(但是你仍然必須安裝 Xcode),則可以在命令行中使用以下命令來運(yùn)行應(yīng)用:
$ react-native run-ios
模擬器運(yùn)行后,點(diǎn)擊調(diào)轉(zhuǎn)RN頁面,會調(diào)轉(zhuǎn)到RN歡迎頁:

4. 報錯問題:
在調(diào)轉(zhuǎn)RN頁面時候可能會遇到以下錯誤

解決辦法:
在info.plist中,add row添加View controller-based status bar appearance并設(shè)置為NO即可。
總結(jié):
至此,以上就是將RN模塊集成到iOS項(xiàng)目的基本步驟,通過以上步驟,可以了解基本的配置以及集成流程, 在我們實(shí)際使用時候,需要根據(jù)自身項(xiàng)目實(shí)際情況來進(jìn)行引用; 接下來讓我們來看下RN和iOS端是如何進(jìn)行交互的.