Integrate ReactNative(0.61) to both iOS and Android
1. Introduction
ReactNative has developed rapidly in recent years and is widely used by major companies. The official documents include the following integration methods:
Integration with Existing Apps
However, integration according to this document will not be successful unless you are strong and have the ability to avoid pits.
Recently, I wrote another series of articles about ReactNative integration:
Becoming the Big Front End series
Here alone out of their own pit avoidance process, I hope to help you.
2. Premises (Required)
The assumed three-terminal project name is:
- iOS: SampleIOS
- Android: SampleAndroid
- ReactNative: IntegrationRN
IOS and Android projects have been created or already exist. The paths are:
Your / / path/SampleIOS/ SampleAnroid/Copy the code
The path can be arbitrary, but if it is different from the one in this article, you need to be aware of the difference between the relative path you configure and the one in this article
3. Create a ReactNative project
Create a project for integration, not for live development. Create it according to the official integration documentation. The differences are as follows:
- The relative paths differ between projects
- Use NPM instead of YARN
Create a project folder
Create IntegrationRN alongside the iOS and Android projects
SampleIOS/
SampleAnroid/
IntegrationRN/
Copy the code
Create package.json and install dependencies
IntegrationRN/
package.json +
Copy the code
Content as follows:
{
"name": "integration-rn"."version": "0.0.1"."private": true."scripts": {
"start": "react-native start"}}Copy the code
Install dependencies
cd path/to/IntegrationRN
npm install react-native --save
Copy the code
Now that we’re done, we need to install the specified version of React. See the output of the previous command:
NPM WARN [email protected] requires a peer of [email protected] but none is installed.
NPM install [email protected] - saveCopy the code
Create index. Js
IntegrationRN/
package.json
index.js +
Copy the code
Content is
import React from 'react';
import {AppRegistry, StyleSheet, Text, View} from 'react-native';
class HelloWorld extends React.Component {
render() {
return (
<View style={styles.container}>
<Text style={styles.hello}>Hello, World</Text>
</View>); }}var styles = StyleSheet.create({
container: {
flex: 1.justifyContent: 'center',},hello: {
fontSize: 20.textAlign: 'center'.margin: 10,}}); AppRegistry.registerComponent('IntegrationRN', () => HelloWorld);
Copy the code
Test Completion
npm run start
Copy the code
4. The iOS integration
The existing project structure is as follows:
Introduce RN dependencies using POD
If your project is already managed with POD, skip it
Install CocoaPods. There are many ways to install CocoaPods
sudo gem install cocoapods
Copy the code
Write Podfile
Create a Podfile under SampleIOS, alongside sampleios.xcodeProj
If you follow the Podfile content provided by the official documentation, since it is too old, you will be called into the Endless pit:
- The React-native relative path needs to be fixed
- Several dependent paths need to be fixed
- Running discover crashes requires adding more dependencies, but which ones and where are the paths
- NativeComponent needs to be added after the end of front-end development.
Stick with the Podfile I’ve been working on
No Bug Podfile
If your project path differs from the preset one, adjust the react_native_path variable
source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '9.0'
use_frameworks!
target 'SampleIOS' do
react_native_path = ".. /IntegrationRN/node_modules/react-native"
pod 'FBLazyVector', :path => "#{react_native_path}/Libraries/FBLazyVector"
pod 'FBReactNativeSpec', :path => "#{react_native_path}/Libraries/FBReactNativeSpec"
pod 'RCTRequired', :path => "#{react_native_path}/Libraries/RCTRequired"
pod 'RCTTypeSafety', :path => "#{react_native_path}/Libraries/TypeSafety"
pod 'React', :path => "#{react_native_path}"
pod 'React-Core', :path => "#{react_native_path}"
pod 'React-Core/DevSupport', :path => "#{react_native_path}"
pod 'React-CoreModules', :path => "#{react_native_path}/React/CoreModules"
pod 'React-RCTActionSheet', :path => "#{react_native_path}/Libraries/ActionSheetIOS"
pod 'React-RCTAnimation', :path => "#{react_native_path}/Libraries/NativeAnimation"
pod 'React-RCTBlob', :path => "#{react_native_path}/Libraries/Blob"
pod 'React-RCTImage', :path => "#{react_native_path}/Libraries/Image"
pod 'React-RCTLinking', :path => "#{react_native_path}/Libraries/LinkingIOS"
pod 'React-RCTNetwork', :path => "#{react_native_path}/Libraries/Network"
pod 'React-RCTSettings', :path => "#{react_native_path}/Libraries/Settings"
pod 'React-RCTText', :path => "#{react_native_path}/Libraries/Text"
pod 'React-RCTVibration', :path => "#{react_native_path}/Libraries/Vibration"
pod 'React-Core/RCTWebSocket', :path => "#{react_native_path}"
pod 'React-cxxreact', :path => "#{react_native_path}/ReactCommon/cxxreact"
pod 'React-jsi', :path => "#{react_native_path}/ReactCommon/jsi"
pod 'React-jsiexecutor', :path => "#{react_native_path}/ReactCommon/jsiexecutor"
pod 'React-jsinspector', :path => "#{react_native_path}/ReactCommon/jsinspector"
pod 'ReactCommon/jscallinvoker', :path => "#{react_native_path}/ReactCommon"
pod 'ReactCommon/turbomodule/core', :path => "#{react_native_path}/ReactCommon"
pod 'Yoga', :path => "#{react_native_path}/ReactCommon/yoga"
pod 'DoubleConversion', :podspec => "#{react_native_path}/third-party-podspecs/DoubleConversion.podspec"
pod 'glog', :podspec => "#{react_native_path}/third-party-podspecs/glog.podspec"
pod 'Folly', :podspec => "#{react_native_path}/third-party-podspecs/Folly.podspec"
end
Copy the code
Then run Pod Install to install the ReactNative dependency
Code integration
Close Xcode and open the project using SampleIOS. Xcworkspace
Next comes easy code integration, modifying the ViewController’s code:
import UIKit
import React
class ViewController: UIViewController {
override func loadView(a) {
let jsCodeURL = URL(string: "http://localhost:8081/index.bundle? platform=ios")!
let rootView = RCTRootView(
bundleURL: jsCodeURL,
moduleName: "IntegrationRN",
initialProperties: [:],
launchOptions: nil
)
self.view = rootView
}
}
Copy the code
Note:
- If you’re running it on a real machine, turn it
localhost
Instead of youReact Development Server
The IP - ModuleName and our previous js code AppRegistry registerComponent consistent
Finally, the info.plist configuration allows HTTP to be loaded
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
Copy the code
After integration, ReactNative’s development interface is run, showing Hello, World
5. Android integration
Configuration gradle
implementation "com.facebook.react:react-native:+"
Copy the code
Configure maven
allprojects {
repositories {
// point to our RN Android source code
maven { url "$rootDir/.. /IntegrationRN/node_modules/react-native/android"}... }}Copy the code
Code integration
To create an Activity, use the following code. Here I use MainActivity directly:
class MainActivity : Activity(), DefaultHardwareBackBtnHandler {
private var mReactRootView: ReactRootView? = null
private var mReactInstanceManager: ReactInstanceManager? = null
override fun onCreate(savedInstanceState: Bundle?). {
super.onCreate(savedInstanceState)
mReactRootView = ReactRootView(this)
mReactInstanceManager = ReactInstanceManager.builder()
.setApplication(application)
.setCurrentActivity(this)
.setBundleAssetName("index.android.bundle")
.setJSMainModulePath("index")
.addPackage(MainReactPackage())
.setUseDeveloperSupport(BuildConfig.DEBUG)
.setInitialLifecycleState(LifecycleState.RESUMED)
.build()
// The string here (e.g. "MyReactNativeApp") has to match
// the string in AppRegistry.registerComponent() in index.jsmReactRootView!! .startReactApplication(mReactInstanceManager,"IntegrationRN".null)
setContentView(mReactRootView)
}
override fun invokeDefaultOnBackPressed(a) {
super.onBackPressed()
}
override fun onPause(a) {
super.onPause() mReactInstanceManager? .onHostPause(this)}override fun onResume(a) {
super.onResume() mReactInstanceManager? .onHostResume(this.this)}override fun onDestroy(a) {
super.onDestroy() mReactInstanceManager? .onHostDestroy(this) mReactRootView? .unmountReactApplication() }override fun onKeyUp(keyCode: Int, event: KeyEvent): Boolean {
if(keyCode == KEYCODE_MENU && mReactInstanceManager ! =null) { mReactInstanceManager!! .showDevOptionsDialog()return true
}
return super.onKeyUp(keyCode, event)
}
override fun onBackPressed(a) {
if(mReactInstanceManager ! =null) { mReactInstanceManager!! .onBackPressed() }else {
super.onBackPressed()
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int.data: Intent){ mReactInstanceManager? .onActivityResult(this, requestCode, resultCode, data)}}Copy the code
SoLoader. The problem of the init
The run encounters the first problem:
java.lang.RuntimeException: SoLoader.init() not yet called
Add to application.oncreate:
SoLoader.init(this.false)
Copy the code
Libhermes. So problem
The operation will run into a second problem, one that has not been officially explained at all
E/AndroidRuntime: FATAL EXCEPTION: create_react_context
Process: com.example.sampleandroid, PID: 9677
java.lang.UnsatisfiedLinkError: couldn't find DSO to load: libhermes.so at com.facebook.soloader.SoLoader.doLoadLibraryBySoName(SoLoader.java:738) at com.facebook.soloader.SoLoader.loadLibraryBySoName(SoLoader.java:591) at com.facebook.soloader.SoLoader.loadLibrary(SoLoader.java:529) at com.facebook.soloader.SoLoader.loadLibrary(SoLoader.java:484) at com.facebook.hermes.reactexecutor.HermesExecutor.
(HermesExecutor.java:20) at com.facebook.hermes.reactexecutor.HermesExecutorFactory.create(HermesExecutorFactory.java:27) at com.facebook.react.ReactInstanceManager$5.run(ReactInstanceManager.java:952) at java.lang.Thread.run(Thread.java:929)
Copy the code
The key information
couldn't find DSO to load: libhermes.so
ReactNative has been using Hermes instead of JavaScriptCore as a JS engine since a certain release
Solution to add hermes dependency package:
implementation "com.facebook.react:react-native:+"
def hermesPath = "$rootDir/.. /IntegrationRN/node_modules/hermes-engine/android/"
debugImplementation files(hermesPath + "hermes-debug.aar")
releaseImplementation files(hermesPath + "hermes-release.aar")
Copy the code
So if I go here and I run it on the emulator, it will load normally and say Hello World
Real machine running problem
The following problems may occur:
The crux of the problem is that ReactNative’s DevSupport module uses localhost loaded JS code, but the React development server is on your computer.
The official document also offers a solution:
adb devices
adb reverse tcp:8081 tcp:8081
Copy the code
But this approach is so bad that different developers have to run this section every time
By looking at the source code of the DevSupport module, we came up with a near-perfect solution:
/ / change debug_http_host
val preferences = PreferenceManager.getDefaultSharedPreferences(applicationContext)
preferences.edit().putString("debug_http_host", WebManager.getWebDevServer()).apply()
Copy the code
Finally, if you are using androidx, you may encounter:
Failed resolution of: Landroidx/swiperefreshlayout/widget/SwipeRefreshLayout;
Just keep adding the dependency
implementation 'androidx. Swiperefreshlayout: swiperefreshlayout: 1.1.0 - alpha02'
Copy the code
This time, finally Hello World
conclusion
Integration process is actually very simple, but want to avoid the pit, consult a lot of information, I hope this can reduce your integration time.
Article source: github.com/zzmingo/Sam…
The last
After the integration, the most important issue is to provide the offline package mechanism, which will be updated in my series of articles. Please follow me.