August 19, 2019 — 7 min read
Fastlane: Painless Mobile App Builds and Deployment
Getting from idea to released app is a long process. As developers, our focus should remain on code and user experience. Releasing a mobile app requires so much more, from screenshot generation to code signing to app store deployments, developing and releasing a mobile app can be a tedious process which Fastlane helps make easier.
When I first got started with React Native, I hoped to focus on learning the intricacies of developing cross-platform apps using my existing knowledge of React and not deal with the headaches I had previously experienced around provisioning profiles, certificates and code signing. Not long into one of my first projects, I discovered fastlane, a way to automate the development and release process for mobile apps.
fastlane is the easiest way to automate beta deployments and releases for your iOS and Android apps. 🚀 It handles all tedious tasks, like generating screenshots, dealing with code signing, and releasing your application.
Currently, I'm using fastlane for code signing and deployment so that is what will be covered in this post but I encourage you to checkout fastlane's available actions to see what else it can automate for you.
Getting Started with Fastlane
First we need to get fastlane installed. However, we need to first install the latest Xcode command line tools if they are not already installed.
$ xcode-select --install
$ brew cask install fastlane
Once installed, we need to create a
fastlane folder inside our React Native project root. Next, create an empty file called
Fastfile. Alternatively, there is a
fastlane init command that can be used to handle some of the scaffolding. Our
Fastfile will consist of all of our lanes which contain fastlane actions that execute synchronously to automate our processes.
fastlane_version '2.129.0' before_all do ensure_git_branch ensure_git_status_clean git_pull end platform :ios do # iOS specific lanes end platform :android do # Android specific lanes end
Fastfile starts with a
before_all hook to ensure our repository has the latest code from our
master branch and is clean so we don't deploy unfinished code. Next, we define the platforms for which we are going to later provide lanes. By defining our platforms individually, we are able to execute builds and deployments for each platform individually.
iOS and the App Store
Let's start with iOS and our first lane which will fetch our App Store certificates and provisioning profiles. We are going to use match to easily sync our certificates and profiles, allowing us to share one code signing identity across our entire development team.
match creates all required certificates & provisioning profiles and stores them in a separate git repository. Every team member with access to the repo can use those credentials for code signing. match also automatically repairs broken and expired credentials. It's the easiest way to share signing credentials across teams
Before creating our lanes, we need to setup match in our project and provide it with the URL to our private Git repo which will hold our code signing identities. Remember, code signing identities SHOULD ALWAYS be kept private.
$ fastlane match init
This will create a
Matchfile in our
fastlane directory within our project root and will be used anytime we use a match action in our lanes. Now let's setup our first lane to fetch our certificates and provisioning files using our
Matchfile settings. Each lane should be proceeded by a description using
desc that clearly describes purpose of the lane. Next, we define the name of our lane and provide it with the actions it should execute.
platform :ios do desc 'Fetch certificates and provisioning profiles' lane :certificates do match(app_identifier: 'com.bundle.identifier', type: 'development', readonly: true) match(app_identifier: 'com.bundle.identifier', type: 'appstore', readonly: true) end end
Now we can run
fastlane ios certificates to automatically save our provisioning profiles and certificate to our macOS keychain for future use. We are now also able to use our
certificates lane as an action in our other iOS-specific lanes. Our next step is to create a lane for building our iOS app and another one for shipping our app to Testflight.
platform :ios do desc 'Fetch certificates and provisioning profiles' lane :certificates do match(app_identifier: 'com.bundle.identifier', type: 'development', readonly: true) match(app_identifier: 'com.bundle.identifier', type: 'appstore', readonly: true) end desc 'Build the iOS application.' private_lane :build do certificates increment_build_number(xcodeproj: './ios/AppName.xcodeproj') gym(scheme: 'AppName', project: './ios/AppName.xcodeproj') end desc 'Ship to Testflight.' lane :beta do build pilot commit_version_bump(message: 'Bump build', xcodeproj: './ios/AppName.xcodeproj') push_to_git_remote end end
Let's break our two new lanes down a bit and start with our build lane. We are using a
private_lane block since it doesn't make sense to only execute a build task directly from the command line using
fastlane build. Inside our build block will call our
certificates lane and execute it before we automatically increment our app build number in Xcode and then build our app using gym.
Now, we have all the pieces in place to ship our app to Testflight using our beta lane. When we run
fastlane ios beta, fastlane executes our build lane to build our signed app
.ipa bundle. We then use pilot to upload our local build to Testflight. Finally, we also create a new commit in our repo for the build number bump and push that to our Git remote automatically.
Android and the Google Play Store
Similar to our iOS-specific lanes, we need to provide a few Android-specific lanes to build and deploy our app to the Google Play Store. Unlike iOS, we don't need to create a specific code signing lane since our Android app will be signed during an assemble task in our build action. This does however assume that we have already setup app signing for our app in the Google Play store. I followed the guide provide by Facebook in the React Native docs before proceeding.
platform :android do desc 'Build the Android application.' private_lane :build do gradle(task: 'clean', project_dir: 'android/') gradle(task: 'assemble', build_type: 'Release', project_dir: 'android/') end end
Our build lane has two gradle actions, the first of which cleans our
android directory before we move on actually building our app using assemble. With our build lane complete, we'll now create lanes for the release tracks on the Google Play Store.
platform :android do desc 'Build the Android application.' private_lane :build do gradle(task: 'clean', project_dir: 'android/') gradle(task: 'assemble', build_type: 'Release', project_dir: 'android/') end desc 'Ship to Play Store Beta.' lane :beta do build supply(track: 'beta', track_promote_to: 'beta') git_commit(path: ['./android/gradle.properties'], message: 'Bump versionCode') push_to_git_remote end desc 'Ship to Play Store Alpha.' lane :alpha do build supply(track: 'alpha', track_promote_to: 'alpha') git_commit(path: ['./android/gradle.properties'], message: 'Bump versionCode') push_to_git_remote end desc 'Ship to Play Store Internal.' lane :internal do build supply(track: 'internal', track_promote_to: 'internal') git_commit(path: ['./android/gradle.properties'], message: 'Bump versionCode', allow_nothing_to_commit: true) push_to_git_remote end end
We now have three lanes, one for each release track. Using
fastlane android OUR_LANE_NAME, each lane calls our
build lane, uses supply to upload that build to the defined Google Play Store track, bumps our app
versionCode and pushes that bump to our Git repo.
Depending on your release flow, you may only need to use the
internal track lane and from there use the Google Play Console to promote the internal release up to the Alpha and Beta tracks before releasing it to the public. However, it can be nice to have the other lanes available if there is ever the need to ship directly to a specific track.
Fastlane to the Rescue
Fastlane has saved my sanity when dealing with code signing and app store deployments for my React Native projects. In a team environment, it's a no brainer to use fastlane actions like match to share certificates and provisioning profiles. Beyond building, code signing and deployment, fastlane has a slew of available actions that can automate your development flow process even more. Whether it's running tests, generating app screenshots, creating automatic documentation or just sending an automated Slack message to the rest of your team that a new build is available, fastlane can help automate all of those processes.