This guide is Part One in our four-part series Using React Native in an Existing iOS App. Click here for Part Two: Dynamic Routing.
RN has a getting started guide, which this closely follows. But in addition to getting an RN View rendered inside your existing iOS app, this guide will also cover passing data into your RN View, provide examples in Objective C and Swift, and cover some of the issues that we faced along the way.
This guide is Part 1 in a series. In future guides we’ll cover how to:
- Use <Navigator/> as a router for multiple RN views inside your app.
- Use Codepush for pushing bug fixes to production in 45 seconds.
- Enable more interactivity with your Native and RN code through notifications, and exporting custom views, and modules.
You can find the full example on Github here: https://github.com/Deliverydotcom/ReactNativeHybridExample.
To get set up, follow the steps in the “Requirements” and “Install React Native Using CocoaPods” sections from the official guide. This will ensure you have Node, Cocoapods, and all other prerequisites to get started.
To keep all of our JS in one place, we’ll create a folder called “ReactComponents” at the root of our project. Let’s keep our node_modules inside this folder.
│ ├── node_modules
│ └── package.json
And our Podfile should contain:
pod ‘React’, :path => ‘./ReactComponents/node_modules/react-native’, :subspecs => [
Note: Add more subspecs as needed. E.g. RCTLinkingIOS is needed to use the LinkingIOS module.
$ pod install
to ensure that your pod is pointing to the right directory. From this point on, make sure you are working in your project’s .xcworkspace.
First we’ll create a class that subclasses UIView (called ReactNativeView). This will serve as our RN View’s wrapper. This class should have one public property called data and one method called initializeReactView that initializes our RN View. In this method we will:
- Set up the data dictionary to pass any data into React.
- Initialize the RCTRootView (provided by RN) with JS code from either our dev server or JS bundle.
- Add the RCTRootView to our wrapper view and set constraints to take up the entire space.
Note: If you’re writing this in Swift, make sure you include RCTRootView.h in your Bridging-Header.
Now that we have our RN View wrapper ready to go, we need to create a class that subclasses UIViewController. Let’s call it ReactNativeViewController. You can do this in a storyboard, nib, or code. Just make sure you have a reference to the ReactNativeView in your UIViewController class. Let’s name that reference reactViewWrapper.
In our ReactNativeViewController’s viewDidLoad method we’re going to set any additional data we want to pass to our JS and then run the initializeReactView method we wrote earlier. In this case we’re passing one property called content that we’ll later render in JS.
With all of that in place, we’re ready to start writing our RN code! Let’s create a file inside our ReactComponents folder called index.ios.js. This serves as our entry point into our RN code. In this file we’ll create a very simple RN app with one component, called ReactNativeExample in this case, and we’ll just render the content passed from our ReactNativeViewController. We have access to the data that was passed as a property called initialProperties in the ReactNativeExample’s props.
And that’s it! Now let’s get this app up and running.
To get up and running we need to start our RN development server. From the root of our project run:
$ (JS_DIR=`pwd`/ReactComponents; cd ReactComponents/node_modules/react-native; npm run start — — root $JS_DIR)
Note: App Transport Security will block requests to localhost by default. If you haven’t done so already, follow this guide to enable them: http://bencoding.com/2015/07/20/app-transport-security-and-localhost/.
In ReactNativeView let’s set the REACT_DEV_MODE to true, compile our app, and navigate to our ReactNativeViewController. You should see the server build your bundle and see your ReactNativeExample component rendered. Every time we save a JS file the server should re-create the bundle.
Note: Shake the device to see a list of developer tools like live reload and Chrome debugging.
Once we’ve finished developing, we are ready to build for release. First, we need to create the main.jsbundle where the JS code will live in production. Let’s go into our ReactComponents folder and run:
$ react-native bundle --dev false --platform ios --entry-file index.ios.js --bundle-output ../main.jsbundle --minify
Note: The first time the main.jsbundle file is created it needs to be manually added to the Xcode project. To do this, find the file in Finder and drag it into the Xcode Project Navigator inside your project folder.
Note: In Release mode, make sure Chrome debugging is disabled or you’ll see a WebSocket connection failure.
In ReactNativeView, let’s set REACT_DEV_MODE to false, compile our app, and we’re good to go!