It took two complete days to install the React Native welcome page on my phone. I should clarify -- it took 1.5 days fighting with Xcode and various Apple developer portals getting nowhere before I found an article that mentioned fastlane
.
After, it took one afternoon. I wrote down each step so other first-time React Native developers find the process less intimidating.
Create a personal Apple ID. You might already have one for iTunes or other Mac products. It should be a personal email address not tied to a company. If it's a work app, you need to create a second Apple ID tied to a work email address.
When you associate an Apple ID email address with a developer account it's associated for life. So make sure to use a personal email for an individual account, or use a work email for a business account.
Purchase an "Individual Membership" for your personal Apple ID. Even if you want to release the app under a business entity, it's such a hassle that I'd suggest deploying first on a personal account and migrate the app to production on a business account down the road.
It's an extra $100 to save time.
Follow the docs and start a new project.
$ react-native init AppName
Let the setup run ...
$ cd AppName
Set up the repository too.
$ git init
$ git add .
$ git commit -m 'initial commit'
Install using Homebrew cask.
$ brew cask install fastlane
Add fastlane to your system path.
# ~/.bashrc
export PATH="$HOME/.fastlane/bin:$PATH"
Open a new terminal window to use the new system path.
Fastlane comes with an interactive setup process.
$ fastlane init
Is this project an iOS project? (y/n)
> y
When you specify the project file don't try to use tab completion, it doesn't work.
Couldn't automatically detect the project file, please provide a path:
> ios/AppName.xcodeproj
Your Apple ID (e.g. [email protected]):
> [email protected]
App IDs usually use your domain name. If the domain name is solely for the app, you can use app
as the actual name com.appname.app
. Otherwise use com.mydomain.appname
.
App Identifier (com.krausefx.app):
> com.mydomain.appname
You might need to select which developer account. Choose your personal one.
Multiple iTunes Connect teams found, please enter the number of the team you want to use:
Note: to automatically choose the team, provide either the iTunes Connect Team ID, or the Team Name in your fastlane/Appfile:
> 1
I can't find docs saying so, but "scheme name" means your main application name.
Optional: The scheme name of your app (If you don't need one, just hit Enter):
> AppName
Created new file './fastlane/Fastfile'. Edit it to manage your own deployment lanes.
You should have a new ./fastlane
directory with Appfile
and Fastfile
. Set your text editor to use Ruby syntax highlighting.
If you have multiple developer accounts, set the iTunes Connect Team ID in Appfile
.
# fastlane/Appfile
itc_team_id "##########"
Create a new private git repository in the cloud. I called mine certs
. Fastlane needs a fresh repository to store Apple code signing certificates.
$ match
To not be asked about this value, you can specify it using 'git_url'
URL to the git repo containing all the certificates:
> [email protected]:USER/REPO.git
If you use GitHub and 2-factor auth, use the SSH repository endpoint. Otherwise the HTTPS repository endpoint should work too. Enter any passcode. You should see files and directories if you refresh the repository online.
All required keys, certificates and provisioning profiles are installed 🙌
Add two entries to .gitignore
for later.
*.cer
*.mobileprovision
Unfortunately, Xcode defaults to automatic certificate management but it doesn't work. Open ./ios/AppName/AppDelegate.m
using Xcode.
Change Identity settings.
Display Name: App Name
Bundle Identifier: com.mydomain.appname
Version: 1.0.0
Build: 1
Under Signing uncheck Automatically manage signing.
Under Signing (Debug) select your development provisioning profile.
Under Signing (Release) select your adhoc provisioning profile.
If there aren't any profiles with your bundle identifier in the dropdown you need to go back to the Match Certs section and actually do it.
Edit ./ios/AppName/AppDelegate.m
to switch the jsCodeLocation depending on the presence of a DEBUG
build flag.
#ifdef DEBUG
jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index.ios" fallbackResource:nil];
#else
jsCodeLocation = [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
#endif
Add a script file ./fastlane/bundle
to bundle the React Native javascript app.
#!/bin/bash
cd ..
react-native bundle --dev false --platform "ios" --entry-file "./index.ios.js" --bundle-output "./ios/main.jsbundle"
Make the script executable.
$ chmod +x fastlane/bundle
You can't install a beta app without adding the device UDID to the provisioning profile. Look up your device name and UDID using iTunes (search online how to). Create a file ios/devices.tsv
where you list each device.
The file extension is .tsv
for tab-separated values despite my markdown processor converting to spaces in the snippet below.
Device ID Device Name
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX Person's iPhone
Configure the beta release process.
# ./fastlane/Fastfile
platform :ios do
desc "Push latest Beta version."
lane :beta do
# Register beta testers.
register_devices(device_file: "ios/devices.tsv")
# Reload provisioning profile.
match(type: "adhoc", force_for_new_devices: true)
# Create missing code signing certs.
cert
# Sign code with provisioning profile.
sigh(adhoc: true)
# Bump Xcode build number.
increment_build_number(xcodeproj: "ios/AppName.xcodeproj")
# Set Xcode app version.
increment_version_number(
version_number: JSON.parse(IO.read("../package.json"))["version"],
xcodeproj: "ios/AppName.xcodeproj"
)
# Bundle React Native javascript.
sh "./bundle"
# Build iOS app from source.
gym(scheme: "AppName", project: "ios/AppName.xcodeproj")
end
end
Build the app and output to your project directory.
$ fastlane ios beta
fastlane.tools finished successfully 🎉
You should see the following outputs.
ios/main.jsbundle
ios/main.jsbundle.meta
AppName.app.dSYM.zip
AppName.ipa
AdHoc_com.mydomain.appname.mobileprovision
You need a way to push new versions. The easiest way for me was via Hockey App. Sign up for an API key and add your test device. In the Hockey App dashboard, go to Account Settings then API Tokens. Create a new token with privileges to upload new apps.
Update your ./fastlane/Fastfile
to push to Hockey App on new builds.
lane :beta do
# ...
hockey(api_key: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx")
end
If the install fails with a generic message, it usually means your certificate or provisioning file is wrong. Make sure the device is in ./ios/devices.tsv
and your Xcode settings use the right provisioning profile and your sigh()
command matches the Xcode settings.
If you want to use TypeScript instead of JavaScript, follow these instructions.
$ yarn add typescript
$ yarn add @types/react
Add a tsconfig.json
for React Native. I chose to put TypeScript source code in ./source
and output compiled JavaScript to ./dist
.
{
"compilerOptions": {
"sourceMap": true,
"moduleResolution": "node",
"target": "es2015",
"module": "es2015",
"jsx": "react",
"allowSyntheticDefaultImports": true,
"rootDir": "source",
"outDir": "dist",
"allowJs": true,
"baseUrl": "source",
"types": [
"react"
]
},
"exclude": [
"node_modules",
"dist",
"index.android.js",
"index.ios.js"
]
}
You probably want to ignore compiled JavaScript.
# .gitignore
/dist
Compile typescript in the ./fastlane/bundle
script.
#!/bin/bash
set -e
cd ..
tsc
react-native bundle --dev false --platform "ios" --entry-file "./index.ios.js" --bundle-output "./ios/main.jsbundle"
When developing, use the watch flag.
$ tsc --watch
Add a main application component.
// source/AppName.tsx
export default class AppName extends Component<{}, {}>
// .. etc ..
Import compiled root ios and android app component classes.
// index.ios.js
import { AppRegistry } from "react-native"
import AppName from "./dist/AppName"
AppRegistery.registerComponent("AppName", () => AppName)
React Native imports the compiled JavaScript. You get type safe code without fighting the build system. Bump the version in package.json
and try out the new TypeScript build.
$ fastlane ios beta
I want to thank the Fastlane team for a phenomenal development tool. I was hesitant to build for native mobile because the release process was an unknown. I chose to solve incremental deployment first, and thanks to Fastlane I feel confident to start building out the app.