ゴール
Xcode, AndroidStudio で適当にさらのネイティブアプリを作成し、任意のアクションによって ReactNative モジュール立ち上げる。
タイトルと乖離があるけど、これができれば既存のアプリを組み込むのも普通にできる。
前提
- iOS それなりにわかる
- 言語は Swift
- Android それなりにわかる
- 言語は Kotlin
- ReactNative それなりにわかる
バージョン情報
$ react-native -v
react-native-cli: 2.0.1
react-native: 0.55.4
手順
RN プロジェクト作成
react-native-cli
で react-native プロジェクトを作成
$ react-native init rn_integrated_native # or your project name
プロジェクトディレクトリに移動
$ cd rn_integrated_native
react-native-cli
で生成されたネイティブモジュールを削除
$ rm -rf ios android
iOS プロジェクトの作成
Xcode でプロジェクトを作成して、先ほど作った RN プロジェクトのルートに保存
$ tree -L 2 -I 'node_modules'
.
├── App.js
├── app.json
├── index.js
├── package.json
├── rn_integrated_native
│ ├── rn_integrated_native
│ └── rn_integrated_native.xcodeproj
└── yarn.lock
3 directories, 5 files
保存した iOS プロジェクトのディレクトリ名を ios
に変更
$ mv rn_integrated_native ios
$ $ tree -L 2 -I 'node_modules'
.
├── App.js
├── app.json
├── index.js
├── ios
│ ├── rn_integrated_native
│ ├── rn_integrated_native.xcodeproj
├── package.json
└── yarn.lock
以上の手順は Android にいても同様
iOS プロジェクトに RN を組み込む
cocoapods
はある
$ pod --version
1.5.0
以下は特に断りのない限り ios
ディレクトリで作業
$ pod init
下記のコードを Podfile
にペースト
platform :ios, '10.0'
plugin 'cocoapods-fix-react-native'
target 'rn_integrated_native' do
use_frameworks!
pod 'React', :path => '../node_modules/react-native', :subspecs => [
'Core',
'CxxBridge',
'DevSupport',
'RCTActionSheet',
'RCTAnimation',
'RCTBlob',
'RCTGeolocation',
'RCTImage',
'RCTNetwork',
'RCTSettings',
'RCTText',
'RCTVibration',
'RCTWebSocket',
]
pod 'yoga', :path => '../node_modules/react-native/ReactCommon/yoga'
pod 'DoubleConversion', :podspec => '../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec'
pod 'glog', :podspec => '../node_modules/react-native/third-party-podspecs/glog.podspec'
pod 'Folly', :podspec => '../node_modules/react-native/third-party-podspecs/Folly.podspec'
end
気づいた人もいるかと思うが、cocoapods-fix-react-native
という gem を使う
cocoapods
を使って react-native の依存を解決しようとした時にエラーが出るという issue があり、それを解決してくれるらしい
しかも、RN のバージョンを見てそれにあったパッチを当ててくれるから、たぶんこれを使っておけば間違いない
- https://github.com/facebook/react-native/issues/17893
- https://github.com/orta/cocoapods-fix-react-native
グローバルに入れたくないので bundler
で gem を管理する
$ cat<<EOF > Gemfile
source "https://rubygems.org"
gem "cocoapods-fix-react-native"
EOF
$ bundle install
$ pod install
これで必要なモジュールは全て入ったので、react-native を乗せた ViewController を立ち上げるまでを見ていく
UIViewController
の extension として、RN を起動するメソッドを追加しておけば、あとは好きなところで呼び出せば良い
import UIKit
import React
extension UIViewController {
func launchReactNativeApp(bundleRoot: String = "index", module: String = "rn_integrated_native", initialProps: NSDictionary? = nil) {
guard let jsCodeLocation = RCTBundleURLProvider.sharedSettings().jsBundleURL(forBundleRoot: bundleRoot, fallbackResource: nil) else {
fatalError("Could not find react-native bundle file!")
}
let rootView = RCTRootView(
bundleURL: jsCodeLocation,
moduleName: module,
initialProperties: initialProps as [NSObject : AnyObject]?
)
let vc = UIViewController()
vc.view = rootView
self.present(vc, animated: true, completion: nil)
}
}
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
@IBAction func onPressButton(_ sender: Any) {
self.launchReactNativeApp()
}
}
iOS には ATS があるので、localhost
を例外に追加する
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSExceptionDomains</key>
<dict>
<key>localhost</key>
<dict> <key>NSTemporaryExceptionAllowsInsecureHTTPLoads</key>
<true/>
</dict>
</dict>
</dict>
// ...
以上で、iOS に関しては当初の目的を達成できた。
Android
疲れたので今度書く