Building React Native Projects with Native Code: Part 1

John Tucker
codeburst
Published in
6 min readOct 9, 2018

--

With Expo (now fully embraced by React Native), building plain (JavaScript only) React Native applications are fairly straightforward. Building with native code, however, is still quite a challenge.

The final version of the application developed through is series is available for download.

Overview

Through this series of articles, we will develop a development workflow (and exploring issues) for building React Native projects with native code. The workflow is roughly based on GitFlow.

Gitflow Workflow is a Git workflow design that was first published and made popular by Vincent Driessen at nvie. The Gitflow Workflow defines a strict branching model designed around the project release. This provides a robust framework for managing larger projects.

Atlassian — Gitflow Workflow

The particular challenge of this (React Native with native code) workflow is that there are two kinds of development work (each requiring their own development tools / environment):

  • React Native (JavaScript): The vast majority of the work is here
  • Native (iOS and Android): A small (hopefully) fraction of the work is here

Let us walk through the branches and workflow:

develop: The authoritative branch for the React Native (JavaScript) code.

develop-native: The authoritative branch for the combined React Native (JavaScript) and native (iOS and Android) code; again we will explain (later) the need for this extra branch

master: The released code; combined React Native (JavaScript) and native (iOS and Android) code

Feature: Feature branches that merge into develop are changes to React Native (JavaScript) only. Likewise, Feature branches that merge into develop-native are changes to native (iOS and Android code) only.

release: The branch used to prepare for an application release (merge into master). One tricky thing here is that changes in this branch need to be merged back into develop-native (all changes) and into develop (only the React Native, JavaScript, commits).

Prerequisites

This article assumes the reader is familiar the basics of:

  • GIT
  • React Native development with Expo

If one is looking to follow along, the software requirements (common across all of the development and target OS environments) for development of feature branches that merge into the develop branch are:

note: Software requirements for development of reature branches that merge into develop-native are described later.

  • GIT; version control system
  • Node.js; Recommend the latest LTS version (currently 8.12.0 LTS)
  • Yarn; Many folks, including myself, have gone back to using the default Node.js package manager (npm). However, I found that the newest version of npm does not work with this solution (believe it was Expo that had the conflict)
  • Watchman; A file watching service; used to trigger builds when files change. On some OS, including the version of Ubuntu (16.0.4 LTS) I am using, you have to configure inotify watchers
  • Expo CLI; Tool for developing apps with Expo. It is less than ideal that Expo requires (tried to avoid this without luck) the globally install expo-cli npm package. The good news is that expo-cli (globally installed) and expo (application dependency) are separately versioned, e.g., 2.2.0 (expo-cli) and 30.0.0 (expo); hopefully they are not tightly coupled

Develop Branch

The first step is to scaffold the application; creating an Expo application (using the blank template):

expo init

We then go ahead and turn the created folder into a GIT repository. We, then, go ahead and create and checkout the develop branch.

Let us update the application to simply display its version; the version is maintained in the app.json file. It is available to the application through Expo’s Constants API.

App.js

We synchronize this version into package.json and update the project name by convention (and will be more important later).

package.json

By simply entering; and opening application using the Expo Client (available on iOS and Android):

expo start

we can have the application running on a device in about a minute.

We go ahead and commit these changes into the develop branch.

Develop-Native Branch

Because our application will contain native modules, we will need to eject to ExpoKit (using the ExpoKit option). Including ExpoKit in the project allows us to continue to use the Expo APIs, over-the-air JavaScript updates, and much of the Expo development process.

note: Because I currently don’t have access to macOS, I will first focus on including an Android native module. Later in this series, I will loop back in and include an iOS native module.

I had assumed that after ejecting we could continue to develop using the normal Expo workflow; turns out that I was wrong. For example, we can no longer use expo start and open the application using Expo Client.

So that we can continue to develop using the normal Expo workflow (using the develop branch), we go ahead and create and checkout (from develop) a develop-native branch. Using this branch, we can then eject.

expo eject

Observations:

  • Android package names are in reverse DNS format by convention, e.g., com.larkintuckerllc.reactnativescaffold
  • The ejection process creates an android folder (lots of files), adds an expokit dependency to the project (package.json), and adds several entries to app.json

app.json

Observations:

  • Could not find documentation on how androidExpoViewUrl is used; it however is not specific to a project and likely is not to be touched
  • scheme: URL scheme to link into your app. For example, if we set this to ‘demo’, then demo:// URLs would open your app when tapped (from Expo documentation).
  • While not documented (and not to be touched), the publishBundlePath and publishManifestPath are path locations where a local copy of published JavaScript bundles are stored; incorporated into the native release build. We later learn that we need to do an Expo publish before the native release build

We go ahead and commit these changes into the develop-native branch; the following build steps will generate a lot of build artifacts that we do not need in our GIT repository.

As we ejected, we now need to develop the application as we would do for any plain React Native application with native code. This is when things get harder; a matrix of specific instructions based on the development and target OS. Unless you have an incredibly fast Internet connection and a development machine with lots of memory and a fast CPU, expect this to take some time.

Observations:

  • My first attempt, using Windows as the development OS was particularly challenging. I ended up switching to Ubuntu in writing this article
  • Later in this series, I will use macOS (and building an iOS application)
  • I was using a physical Android device (the emulator was too slow on my older development machine) and ended up following the React Native instructions; Running on Device

After confirming that my development machine and test device were properly setup for React Native with native code development, we begin development of our ejected application by starting the local React Native Packager server (serves up the JavaScript bundles) is running:

expo start

We then open the newly created android folder as a new project in Android Studio. We can run the application from Android Studio or from the command line.

If you prefer to use the command line, you can run ./gradlew installDevMinSdkDevKernelDebug from inside the android directory to build the project and install it on the running device/emulator.

Expo — Developing with ExpoKit

Observations:

  • The first time you open an ejected Expo project in Android Studio, expect it to take several minutes to download all the dependencies.
  • When opening the project, Android Studio may prompt you to upgrade the version of Gradle or other build tools, but don’t do this as you may get unexpected results. ExpoKit always ships with the latest supported versions of all build tools (from Developing with ExpoKit)
  • When starting the application from Android Studio, do not activate Instant Run as that changes the platform; which is not compatible with React Native
  • In addition to being much slower, I often got errors when trying to run the application from Android Studio; found the command line method much more reliable

Master Branch

As the master branch is what we will be using to build the production application, we can go ahead and merge in the develop-native branch (without the build artifacts) into master.

Next Steps

In the next article, Building React Native Projects with Native Code: Part 2, we will explore the workflow involving feature branches merging into develop.

--

--