React Native has gained a lot of popularity since it was introduced and open-sourced by Facebook early this year. The framework uses JavaScript to build mobile apps and now supports both iOS and Android.

After playing with the framework, I decided to write a simple iOS app using react native and the first thing I wanted to do was to setup an automated deployment pipeline so that everytime I push new changes to my git repository, my continuous integration server can run the tests and upload the new app build to TestFlight.

I decided to use Fastlane which is an iOS automation toolset, recently acquired by Twitter and is now a part of Fabric.

In this post I will go through the steps of creating a very simple React Native app and configuring Fastlane to deploy the app to TestFlight.

Create Hello World React Native App

Lets start by creating a simple React Native app which will show ‘Hello World’ text.

If you do not have React Native installed on your machine, please follow these instructions.

After installing React Native, you can use the CLI to create a new project ‘helloWorldApp’ by running following command.

1
$ react-native init helloWorldApp

This command will fetch all the dependencies, including React Native source code and will create a new Xcode project for iOS and gradle project for Android.

Open ios/helloWorldApp.xcodeproj in Xcode and hit run button or press ⌘+r to run the project.

React Native entry file for the iOS app is index.ios.js . Open this file in your favourite editor and change the content of the render function to

1
2
3
4
5
6
7
8
render: function() {
return (
<View style={styles.container}>
<Text style={styles.welcome}>
Hello World
</Text>
</View>
);

You can press ⌘+r to reload the app in the simulator for quick feedback cycle.

Setup Fastlane for automated deployment

Now we have the hello world app working, we will setup Fastlane to work with our app.

Create offline bundle

The AppDelegate class initialise the app and load React Native JavaScript code. When the app is running in iPhone simulator, AppDelegate loads the JavaScript code from development server. To run the app without development server, we have to create an offline bundle and pack all the JavaScript code within the app. For this we can use React Native CLI bundle command, which will minify and bundle the JavaScript code to main.jsbundle

1
$ react-native bundle --entry-file ./index.ios.js --platform ios --bundle-output ios/main.jsbundle

Now update the AppDelegate.m (ios/helloWorldApp/AppDelegate.m).

replace

1
jsCodeLocation = [NSURL URLWithString:@"http://localhost:8081/index.ios.bundle?platform=ios&dev=true"];

with

1
2
3
4
5
#ifdef DEBUG
jsCodeLocation = [NSURL URLWithString:@"http://localhost:8081/index.ios.bundle?platform=ios&dev=true"];
#else
jsCodeLocation = [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
#endif

Initialise Fastlane

Follow these instructions to install Fastlane.

Once Fastlane is installed, run ‘fastlane init’ command to setup fastlane and create all the necessary files and folders. For this example, I have skipped delivery and snapshot steps.

Update fastlane/Fastfile file and replace beta config with this.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
lane :beta do
sigh

produce(
username: '<Apple ID>',
app_identifier: 'com.amitkothari.helloWorldApp', # Explicit app id
app_name: 'Unique App Name',
language: 'English',
app_version: '1.0',
sku: 'HWAPP',
team_name: '<Team Name>' # only required when the Apple ID is assocaited with multiple teams
)

increment_build_number(
xcodeproj: './ios/helloWorldApp.xcodeproj'
)

gym(
scheme: 'helloWorldApp',
project: './ios/helloWorldApp.xcodeproj'
)

pilot
end

Updated on 8 December 2015

While Fastlane will take care of most of the stuff, we still need to register our app in Apple’s developer center. Login to developer center and setup the app in iOS Provisioning Portal and iTunes Connect with an explicit app id.

We are using Fastlane produce to register our app on Dev portal and iTunes Connect.

We also need to setup our app to auto increment app’s build and version number.
Follow these two steps to enable agvtool and set up version and build number.

While you have the Xcode open, also set the bundle identifier (com.amitkothari.helloworldapp) and team under the general tab.

We also need to add app icon and launch screen images before uploading the app to TestFlight.

We can now create another entry in the package.json scripts, to bundle and deploy the app in a single command.

1
2
3
4
5
6
7
8
9
10
11
12
{
"name": "helloWorldApp",
"version": "0.0.1",
"private": true,
"scripts": {
"start": "react-native start",
"deploy": "react-native bundle --entry-file ./index.ios.js --platform ios --bundle-output ios/main.jsbundle && fastlane beta"
}
,

"dependencies": {
"react-native": "^0.15.0"
}

}

Additional steps

  • If your apple id is associated with multiple teams. You may get a prompt to select the team while running Fastlane command. To avoid this update fastlane/Appfile and enter team id and name.

  • If you get exportArchive or IDEDistributionErrorDomain error similar to this issue, add ‘use_legacy_build_api: true’ under gym configuration.

1
2
3
4
5
gym(
scheme: 'helloWorldApp',
project: './ios/helloWorldApp.xcodeproj'
use_legacy_build_api: true
)
  • If you get the following error

“Invalid Provisioning Profile. The provisioning profile included in the bundle com.amitkothari.helloWorldApp [Payload/helloWorldApp.app] is invalid. [Missing code-signing certificate]. A Distribution Provisioning profile should be used when submitting apps to the App Store. For more information, visit the iOS Developer Portal.”

Open the app in Xcode and under the build setting tab, set code signing provisioning profile to “$(PROFILE_UUID)” . See the image below or read this for more details.

Set provisiong profile in Xcode