React Native [0.45.1] Intergrating with existing apps

Purpose

This repository is made for React Natvie users who want to integrate RN[0.45.1] to exisiting application using Swift3, Obbjective-C, JAVA, Kotlin. Integration with existing apps is well documented, but I want to make it more easily. This document is tested recently. Feel free to make Pull Request.
Thanks.

Develop Enviroment

This document is based on below version. React Native currenly changes a lot, so be careful.

1
2
3
4
"dependencies": {
"react": "16.0.0-alpha.12",
"react-native": "0.45.1"
}

If you have iOS and Android application independently, this will be perfect guide for you. Please follow these steps.

Getting started

Step 1. Install tools and init project

Install tools

you can also see this in Getting Started

1
2
3
brew install node
brew install watchman
npm install -g react-native-cli

If you all done this process, init your project.

1
react-native init ReactProject

In your ReactProject directory, you can find package.json. this should be look like this.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
{
"name": "ReactProject",
"version": "0.0.1",
"private": true,
"scripts": {
"start": "node node_modules/react-native/local-cli/cli.js start",
"test": "jest"
},
"dependencies": {
"react": "16.0.0-alpha.6",
"react-native": "0.44.0"
},
"devDependencies": {
"babel-jest": "20.0.3",
"babel-preset-react-native": "1.9.2",
"jest": "20.0.4",
"react-test-renderer": "16.0.0-alpha.6"
},
"jest": {
"preset": "react-native"
}
}

you can’t use expo when you integrate with exisiting apps.


Step 2. Add postinstall in package.json

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
{
"name": "ReactProject",
"version": "0.0.1",
"private": true,
"scripts": {
"postinstall": "sed -i '' 's/#import <RCTAnimation\\/RCTValueAnimatedNode.h>/#import \"RCTValueAnimatedNode.h\"/' ./node_modules/react-native/Libraries/NativeAnimation/RCTNativeAnimatedNodesManager.h",
"start": "node node_modules/react-native/local-cli/cli.js start",
"test": "jest"
},
"dependencies": {
"react": "16.0.0-alpha.12",
"react-native": "0.45.1"
},
"devDependencies": {
"babel-jest": "20.0.3",
"babel-preset-react-native": "2.0.0",
"jest": "20.0.4",
"react-test-renderer": "16.0.0-alpha.12"
},
"jest": {
"preset": "react-native"
}
}

Currently, RN 0.45.1 have a problem in iOS - #import <RCTAnimation\\/RCTValueAnimatedNode.h>. You need to change it to #import "RCTValueAnimatedNode.h. So use postinstall


Step 3. Git clone your iOS and Android Project

Main purpose of this step is to make structure for cofiguration management. You can just copy and paste to /android directory and /iOS directory. or git clone to make below srtucture.

1
2
3
4
[React native repository]
|
|- /android [Android repository]
|- /ios [iOS repository]


Step4. Modify iOS project

You can do easily beacuse iOS depedency tool is cocoapods. Add libraries in Podfile and pod install

1
2
3
4
5
6
7
8
9
10
11
12
#React Native
pod 'React', :path => '../node_modules/react-native', :subspecs => [
'Core',
'RCTText',
'RCTNetwork',
'RCTImage',
'RCTWebSocket',
'DevSupport',
'BatchedBridge',
'RCTAnimation'
]
pod 'Yoga', :path => '../node_modules/react-native/ReactCommon/yoga'

Make UIViewController to present React Native view

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import UIKit
import React
import SnapKit
class ReactViewController: UIViewController, ZBAnSimPopupDelegate {
var reactView: RCTRootView!
var item_id: Int?
override func loadView() {
super.loadView()
guard let id = item_id else { return }
let mockData = ["item_id": "\(id)"]
self.reactView = ReactBridge.shared.viewForModule("ReactProject", initialProperties: mockData)
self.view.addSubview(self.reactView)
self.reactView.snp.makeConstraints { (m) in
m.edges.equalTo(self.view)
}
}
}

Add React Bridge to communicate with index.ios.js <-> UIViewController. forHotReloading flag is for compile bundle or localhost.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
let localUrl = "http://localhost:8081/index.ios.bundle?platform=ios&dev=true"
let forHotReloading = false
class ReactBridge: NSObject {
static let shared = ReactBridge()
}
extension ReactBridge: RCTBridgeDelegate {
func sourceURL(for bridge: RCTBridge!) -> URL! {
return URL(string: localUrl)
}
func createBridgeIfNeeded() -> RCTBridge {
let bridge = RCTBridge.init(delegate: self, launchOptions: nil)
return bridge ?? RCTBridge()
}
func viewForModule(_ moduleName: String, initialProperties: [String : Any]?) -> RCTRootView {
if forHotReloading {
let viewBridge = createBridgeIfNeeded()
let rootView: RCTRootView = RCTRootView(
bridge: viewBridge,
moduleName: moduleName,
initialProperties: initialProperties)
return rootView
} else {
if let iosBundle = Bundle.main.url(forResource: "main", withExtension: "jsbundle") {
guard let bundleRootView = RCTRootView(bundleURL: iosBundle, moduleName: moduleName, initialProperties: initialProperties, launchOptions: nil) else { return RCTRootView() }
return bundleRootView
}
}
return RCTRootView()
}
}

Add ReactManager.swift to call method with Native <-> React-Native

1
2
3
4
5
6
7
8
9
10
11
12
@objc(ReactManager) class ReactManager: NSObject {
var bridge: RCTBridge!
@objc func back(_ reactTag: NSNumber) {
DispatchQueue.main.async {
if let view = self.bridge.uiManager.view(forReactTag: reactTag) {
let presentedViewController = view.reactViewController()
presentedViewController?.navigationController?.pop(animated: true)
}
}
}
}

We need addtional Objective-C file beacuse React use macro. Add ReactManagerBridge.m file like this

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#import "ReactManagerBridge.h"
#import "zigbang-Swift.h"
@implementation ReactManagerBridge
RCT_EXPORT_MODULE(ReactManager);
RCT_EXPORT_METHOD(back:(nonnull NSNumber *)reactTag) {
ReactManager* reactManager = [[ReactManager alloc] init];
reactManager.bridge = _bridge;
[reactManager back:reactTag];
}
@end


Step4. Modify Android project

add dependency in build.gradle

1
2
3
4
5
6
7
8
9
10
allprojects {
repositories {
mavenLocal()
jcenter()
maven {
// All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
url "$rootDir/../node_modules/react-native/android"
}
}
}

in your app.gradle. I added exclude becuase it conflict with exisiting dependecies

1
2
3
compile ("com.facebook.react:react-native:+") {
exclude group:'com.facebook.stetho', module:'stetho'
}

Add ReactInstanceManager to use it in React Native. I recommand to make instance in Mainapplication becuase it takes some time to generate instance

1
2
3
4
5
6
7
8
9
10
11
12
public static ReactInstanceManager react;
this.react = ReactInstanceManager.builder()
.setApplication(this)
.setBundleAssetName("index.android.bundle")
.setJSMainModuleName("index.android")
.addPackage(new MainReactPackage())
.addPackage(new ReactManagerBridge())
.setUseDeveloperSupport(BuildConfig.DEBUG)
.setInitialLifecycleState(LifecycleState.RESUMED)
.build();

Modify your ReactRootView where you want to use. Also add props to pass parameter

1
2
3
4
5
6
7
8
@BindView(R.id.container)
ReactRootView container;
react = MainApplication.react;
Bundle props = new Bundle();
props.putString("items", new Gson().toJson(response));
container.startReactApplication(react, "ReactProject", props);

Add bridge to communicate with Native

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class ReactManagerBridge() : ReactPackage {
override fun createJSModules(): List<Class<out JavaScriptModule>> {
return emptyList()
}
override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> {
val modules = ArrayList<ViewManager<*, *>>()
return modules
}
override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> {
val modules = ArrayList<NativeModule>()
modules.add(ReactManager(reactContext))
return modules
}
}

Add reactmanager to call method

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class ReactManager(reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) {
fun ReactManager(reactContext: ReactApplicationContext) {
super.getReactApplicationContext()
}
override fun getName(): String {
return "ReactManager"
}
@ReactMethod
fun alert(message: String) {
Toast.makeText(getReactApplicationContext(), message, Toast.LENGTH_LONG).show()
}
}

This is all about setting your react native to exisiting application!