└── README.md /README.md: -------------------------------------------------------------------------------- 1 | # React Native [0.45.1] Integration with the existing apps 2 | 3 | ## Purpose 4 | This repository was made for React Natvie users who want to integrate RN[0.45.1] to the existing application using Swift3, Obbjective-C, JAVA, Kotlin. [Integration with existing apps](http://facebook.github.io/react-native/releases/next/docs/integration-with-existing-apps.html#integration-with-existing-apps) is well documented, but I wanted to make it more easy. This document was recorded after the recent test. Feel free to make Pull Request. 5 | Thanks. 6 | 7 | ## Develop Enviroment 8 | This document is based on below version. React Native currently changes a lot, so be careful. 9 | 10 | ```javascript 11 | "dependencies": { 12 | "react": "16.0.0-alpha.12", 13 | "react-native": "0.45.1" 14 | } 15 | ``` 16 | If you have `iOS` and `Android` application independently, this will be the perfect guide for you. Please follow these steps. 17 | 18 | ## Getting started 19 | ### Step 1. Install tools and init project 20 | #### Install tools 21 | you can also see this in [Getting Started](http://facebook.github.io/react-native/releases/next/docs/getting-started.html) 22 | ``` 23 | brew install node 24 | brew install watchman 25 | npm install -g react-native-cli 26 | ``` 27 | 28 | If you are all done with the above, init your project. 29 | ``` 30 | react-native init ReactProject 31 | ``` 32 | In your `ReactProject` directory, you can find `package.json`. this should look like this. 33 | ```javascript 34 | { 35 | "name": "ReactProject", 36 | "version": "0.0.1", 37 | "private": true, 38 | "scripts": { 39 | "start": "node node_modules/react-native/local-cli/cli.js start", 40 | "test": "jest" 41 | }, 42 | "dependencies": { 43 | "react": "16.0.0-alpha.6", 44 | "react-native": "0.44.0" 45 | }, 46 | "devDependencies": { 47 | "babel-jest": "20.0.3", 48 | "babel-preset-react-native": "1.9.2", 49 | "jest": "20.0.4", 50 | "react-test-renderer": "16.0.0-alpha.6" 51 | }, 52 | "jest": { 53 | "preset": "react-native" 54 | } 55 | } 56 | ``` 57 | 58 | > you can't use `expo` when you integrate with existing apps. 59 | 60 | --- 61 | ### Step 2. Add postinstall in package.json 62 | ```javascript 63 | { 64 | "name": "ReactProject", 65 | "version": "0.0.1", 66 | "private": true, 67 | "scripts": { 68 | "postinstall": "sed -i '' 's/#import /#import \"RCTValueAnimatedNode.h\"/' ./node_modules/react-native/Libraries/NativeAnimation/RCTNativeAnimatedNodesManager.h", 69 | "start": "node node_modules/react-native/local-cli/cli.js start", 70 | "test": "jest" 71 | }, 72 | "dependencies": { 73 | "react": "16.0.0-alpha.12", 74 | "react-native": "0.45.1" 75 | }, 76 | "devDependencies": { 77 | "babel-jest": "20.0.3", 78 | "babel-preset-react-native": "2.0.0", 79 | "jest": "20.0.4", 80 | "react-test-renderer": "16.0.0-alpha.12" 81 | }, 82 | "jest": { 83 | "preset": "react-native" 84 | } 85 | } 86 | ``` 87 | Currently, RN 0.45.1 have a problem in `iOS - #import `. You need to change it to `#import "RCTValueAnimatedNode.h`. So use `postinstall` 88 | 89 | --- 90 | ### Step 3. Git clone your iOS and Android Project 91 | Main purpose of this step is to make a structure for configuration management. You can just copy and paste to `/android` directory and `/iOS` directory. or `git clone` to make a structure as below. 92 | ``` 93 | [React native repository] 94 | | 95 | |- /android [Android repository] 96 | |- /ios [iOS repository] 97 | ``` 98 | 99 | --- 100 | ### Step4. Modify iOS project 101 | You can do this step easily with the iOS depedency tool `cocoapods`. Add libraries in `Podfile` and `pod install` 102 | ```ruby 103 | #React Native 104 | pod 'React', :path => '../node_modules/react-native', :subspecs => [ 105 | 'Core', 106 | 'RCTText', 107 | 'RCTNetwork', 108 | 'RCTImage', 109 | 'RCTWebSocket', 110 | 'DevSupport', 111 | 'BatchedBridge', 112 | 'RCTAnimation' 113 | ] 114 | pod 'Yoga', :path => '../node_modules/react-native/ReactCommon/yoga' 115 | ``` 116 | Make `UIViewController` to present `React Native view` 117 | ```Swift 118 | import UIKit 119 | import React 120 | import SnapKit 121 | 122 | class ReactViewController: UIViewController, ZBAnSimPopupDelegate { 123 | var reactView: RCTRootView! 124 | var item_id: Int? 125 | 126 | override func loadView() { 127 | super.loadView() 128 | 129 | guard let id = item_id else { return } 130 | let mockData = ["item_id": "\(id)"] 131 | 132 | self.reactView = ReactBridge.shared.viewForModule("ReactProject", initialProperties: mockData) 133 | self.view.addSubview(self.reactView) 134 | 135 | self.reactView.snp.makeConstraints { (m) in 136 | m.edges.equalTo(self.view) 137 | } 138 | } 139 | } 140 | ``` 141 | Add `React Bridge` to communicate with `index.ios.js` <-> `UIViewController`. `forHotReloading` flag is for compile bundle or localhost. 142 | ```Swift 143 | let localUrl = "http://localhost:8081/index.ios.bundle?platform=ios&dev=true" 144 | let forHotReloading = false 145 | 146 | class ReactBridge: NSObject { 147 | static let shared = ReactBridge() 148 | } 149 | extension ReactBridge: RCTBridgeDelegate { 150 | func sourceURL(for bridge: RCTBridge!) -> URL! { 151 | return URL(string: localUrl) 152 | } 153 | 154 | func createBridgeIfNeeded() -> RCTBridge { 155 | let bridge = RCTBridge.init(delegate: self, launchOptions: nil) 156 | return bridge ?? RCTBridge() 157 | } 158 | 159 | func viewForModule(_ moduleName: String, initialProperties: [String : Any]?) -> RCTRootView { 160 | if forHotReloading { 161 | let viewBridge = createBridgeIfNeeded() 162 | let rootView: RCTRootView = RCTRootView( 163 | bridge: viewBridge, 164 | moduleName: moduleName, 165 | initialProperties: initialProperties) 166 | return rootView 167 | } else { 168 | if let iosBundle = Bundle.main.url(forResource: "main", withExtension: "jsbundle") { 169 | guard let bundleRootView = RCTRootView(bundleURL: iosBundle, moduleName: moduleName, initialProperties: initialProperties, launchOptions: nil) else { return RCTRootView() } 170 | return bundleRootView 171 | } 172 | } 173 | return RCTRootView() 174 | } 175 | } 176 | ``` 177 | Add `ReactManager.swift` to call method with `Native` <-> `React-Native` 178 | ```Swift 179 | @objc(ReactManager) class ReactManager: NSObject { 180 | var bridge: RCTBridge! 181 | 182 | @objc func back(_ reactTag: NSNumber) { 183 | DispatchQueue.main.async { 184 | if let view = self.bridge.uiManager.view(forReactTag: reactTag) { 185 | let presentedViewController = view.reactViewController() 186 | presentedViewController?.navigationController?.pop(animated: true) 187 | } 188 | } 189 | } 190 | } 191 | ``` 192 | We need an addtional `Objective-C file` beacuse `React` uses macro. Add `ReactManagerBridge.m` file like this 193 | ```Swift 194 | #import "ReactManagerBridge.h" 195 | #import "zigbang-Swift.h" 196 | 197 | @implementation ReactManagerBridge 198 | 199 | RCT_EXPORT_MODULE(ReactManager); 200 | 201 | RCT_EXPORT_METHOD(back:(nonnull NSNumber *)reactTag) { 202 | ReactManager* reactManager = [[ReactManager alloc] init]; 203 | reactManager.bridge = _bridge; 204 | [reactManager back:reactTag]; 205 | 206 | } 207 | @end 208 | ``` 209 | 210 | --- 211 | ### Step4. Modify Android project 212 | 213 | add dependency in `build.gradle` 214 | 215 | ``` 216 | allprojects { 217 | repositories { 218 | mavenLocal() 219 | jcenter() 220 | maven { 221 | // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm 222 | url "$rootDir/../node_modules/react-native/android" 223 | } 224 | } 225 | } 226 | ``` 227 | in your `app.gradle`. I added `exclude` becuase it conflicts with existing dependecies 228 | 229 | 230 | ``` 231 | compile ("com.facebook.react:react-native:+") { 232 | exclude group:'com.facebook.stetho', module:'stetho' 233 | } 234 | 235 | ``` 236 | 237 | Add `ReactInstanceManager` to use it in `React Native`. I recommend to make an instance in `Mainapplication` because it takes some time to generate an instance 238 | 239 | ```Java 240 | public static ReactInstanceManager react; 241 | 242 | 243 | this.react = ReactInstanceManager.builder() 244 | .setApplication(this) 245 | .setBundleAssetName("index.android.bundle") 246 | .setJSMainModuleName("index.android") 247 | .addPackage(new MainReactPackage()) 248 | .addPackage(new ReactManagerBridge()) 249 | .setUseDeveloperSupport(BuildConfig.DEBUG) 250 | .setInitialLifecycleState(LifecycleState.RESUMED) 251 | .build(); 252 | ``` 253 | 254 | Modify your `ReactRootView` where you want to use. Also, add props to pass parameter 255 | ```Java 256 | @BindView(R.id.container) 257 | ReactRootView container; 258 | 259 | react = MainApplication.react; 260 | 261 | Bundle props = new Bundle(); 262 | props.putString("items", new Gson().toJson(response)); 263 | container.startReactApplication(react, "ReactProject", props); 264 | 265 | ``` 266 | 267 | Add bridge to communicate with Native 268 | 269 | ```Java 270 | class ReactManagerBridge() : ReactPackage { 271 | 272 | override fun createJSModules(): List> { 273 | return emptyList() 274 | } 275 | 276 | override fun createViewManagers(reactContext: ReactApplicationContext): List> { 277 | val modules = ArrayList>() 278 | return modules 279 | } 280 | 281 | override fun createNativeModules(reactContext: ReactApplicationContext): List { 282 | val modules = ArrayList() 283 | 284 | modules.add(ReactManager(reactContext)) 285 | return modules 286 | } 287 | 288 | } 289 | ``` 290 | 291 | Add reactmanager to call method 292 | 293 | ```Java 294 | class ReactManager(reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) { 295 | 296 | fun ReactManager(reactContext: ReactApplicationContext) { 297 | super.getReactApplicationContext() 298 | } 299 | 300 | override fun getName(): String { 301 | return "ReactManager" 302 | } 303 | 304 | @ReactMethod 305 | fun alert(message: String) { 306 | Toast.makeText(getReactApplicationContext(), message, Toast.LENGTH_LONG).show() 307 | } 308 | } 309 | ``` 310 | 311 | This is all about setting your react native to existing application! 312 | 313 | 314 | 315 | --------------------------------------------------------------------------------