The idea to implement RN came from a Course Hero internal hackathon project done by myself and Ernesto Rodriguez. We saw the opportunity to introduce Course Hero to this great technology, already in use at Shopify, Facebook, Instagram, Tesla, and more.
Though Course Hero currently uses React for web development, we also have separate mobile teams who maintain our mobile native apps. Using RN allowed web developers with good knowledge of React to apply their expertise towards building a mobile app. This flexibility allowed us to scale our textbook product to native platforms in order to give our customers a great experience.
Deep dive on the integration
When we started, we had a separate repository on GitLab: one for our web app, and another for our iOS app. We created a separate repository for the RN integration, which had the build file. There is no easy way of creating the two links, other than having them in a remote somewhere and fetching the build from a script inside the iOS repo or adding the RN inside the iOS repo. But we didn’t want the iOS team needing to clone any RN dependencies, and this was our first iteration anyway.
We started by adding the RN dependencies to the iOS Podfile. We then forked the RN project to our Course Hero Github Repo and then used the source method to clone the RN project to our local
~/.cocoapods/repos/coursehero dir. Now everyone who clones the iOS repo will automatically have the RN dependencies when doing
In Github, we made 0.63-stable our default branch. This helped us keep the RN project in sync with the Podfile. To change the default branch in GitHub: [repo] -> Settings -> Branches
Intro to RCTRootView
Doing the integration between the two sides is fairly simple. In iOS, we can use the subclass RCTRootView from the UIView class, which we can use in any location of our iOS app.
Most of all the Swift and the Obj-c code below is under the CourseHero iOS folder.
How do the two worlds communicate?
For the RN and native applications to communicate, we need a bridge — a way to send JSON data bidirectionally and asynchronously.
In our case, the RN app had a few modules that we needed to implement. From sending user information to sending callbacks and executing some business logic on the native side.
RN To Native
A key step in the process was creating a Native Module, which is a 3 step process.
The first step is to tell our native app about the RN bridge (we only need to perform this once), and then add the data below to the header.h file in our project. Note that there should only be one header file per project and it should conform to the standard naming convention,
Next, we create our Modules. We started with
TrackingModule.swift which allowed us to access the Native code from the RN side and report some tracking metrics to our internal tracking service.
Finally, we exposed the Swift class Module to RN by creating another file, typically the same name of the module above but with a
.m extension representing Objective-C. This is typically referred to as an RN Macro.
Accessing the Swift module from React Native
With the native side set up, we moved to the RN
project/App.js file, where we imported
NativeModules from the
react-native package. Any module exported from the Obj-C Macros will be available using the
To recap, the process of creating a Native Module and exposing it to RN goes like this:
1. Create the Swift Module Class
2. Obj-C Macro which expose the Swift Module Class
3. NativeModules which is used in RN app, to access the module or methods exported from Objective-C* @objc in the top of a swift method, is to export them to the Objective-C Class
* RCT_EXTERN_MODULE or RCT_EXPORT_MODULE (from objective-c code) - to export the module or methods to the RN
Native To React Native
When we instantiate
RCTRootView , we can pass data into the
initialProperties parameter. The data needs to be a
NSDictionary, which then gets converted to a JSON object that we can access in the root component.
RCTRootView exposes another way of sending messages by using
appProperties, which is helpful if you want to update the properties initialized in your
RCTRootView and trigger a rerender of the root component.
We didn’t have a use case for using the
RCTEventEmitter subclass, but this is the preferred way of emitting some events to signal that something has changed to the RN side.
RN allowed us to build, integrate and deploy the textbook app to the existing iOS app in less than a month. While doing the integration, we took advantage of hot reloading which allowed us to see the changes being made in RN almost instantly, compared to >20 seconds that native code would typically take to build.
By putting just a little effort to integrate React Native into our application stack, we quickly realized the advantages it would bring to our organization. There may be cases where React Native is not the right choice, but for us, it works great for our Textbook Solutions product and we look forward to building others using this technology. We hope this summary helps you get started on your React Native integration journey.