Fastlane
fastlane is an open-source Ruby tool that automates building, signing,
testing and releasing native apps. Steps are actions (build_app,
upload_to_testflight, match), grouped into lanes in a Fastfile, and
run the same way on a laptop and in CI. In React Native it is used for bare
projects and for Expo projects built without EAS (on the native projects
generated by npx expo prebuild); EAS Build and EAS Submit are Expo's
cloud alternative.
Details
What It Automates
| Task | Actions |
|---|---|
| iOS code signing | match: certificates and profiles in a private encrypted git repo or cloud storage, shared by the team and CI |
| Build | build_app (gym) for iOS, gradle for Android |
| Versioning | increment_build_number, increment_version_code (plugin) |
| Beta distribution | upload_to_testflight, upload_to_play_store(track: 'internal'), Firebase App Distribution (plugin) |
| Store release | upload_to_app_store (deliver), upload_to_play_store (supply), metadata and release notes |
| Screenshots | capture_screenshots (snapshot) across devices and languages |
Fastlane vs EAS
| fastlane | EAS Build / Submit | |
|---|---|---|
| Runs on | Your machines or CI (iOS builds need macOS) | Expo's cloud, or locally with --local |
| Signing | match or manual | Credentials managed by EAS |
| Fits | Any native project, full control | Expo and bare projects; paid tiers for more builds |
Example
lane :beta_ios do
match(type: "appstore") # Fetch the certificate and profile
increment_build_number(xcodeproj: "ios/MyApp.xcodeproj")
build_app(workspace: "ios/MyApp.xcworkspace", scheme: "MyApp")
upload_to_testflight
end
lane :beta_android do
# Play rejects a second upload with the same versionCode
increment_version_code(gradle_file_path: "android/app/build.gradle") # plugin
gradle(task: "clean bundleRelease", project_dir: "android")
upload_to_play_store(track: "internal", aab: "android/app/build/outputs/bundle/release/app-release.aab")
end
Follow-up Questions
- How does CI get the certificates?
match(readonly: true)fetches them from the match repository and decrypts them withMATCH_PASSWORD; repository access and the App Store Connect API key (for uploads) are CI secrets. - Why do iOS builds need macOS? Xcode runs only on macOS, hence macOS runners or EAS Build.
Common Mistakes
- Not bumping the build number or
versionCode: the store rejects a build with a number it already has. - Committing certificates or keystore passwords instead of using
matchand CI secrets.