Originally published on PEAKIQ
Source: https://www.peakiq.in/blog/automating-app-updates-in-react-native-with-fastlane-and-remote-config
Automating App Updates in React Native with Fastlane and Remote Config
Managing app updates across iOS and Android is repetitive work. Fastlane automates the build and release pipeline, while Firebase Remote Config lets you enforce a minimum required version without shipping a new build to change the threshold. Together they give you a reliable, low-maintenance update system.
This guide walks through setting up both, then connecting them via a GitHub Actions workflow.
Prerequisites
- A React Native project
- A Firebase project with Remote Config enabled
- Fastlane installed locally
- Basic familiarity with GitHub Actions
Step 1 — Set Up Fastlane
Initialize Fastlane in your project root:
fastlane init
Follow the prompts to configure it for your project. This creates a fastlane/ directory containing a Fastfile and an Appfile.
Update your Fastfile with lanes for both iOS and Android. Each lane should handle the steps relevant to your release process — incrementing the build number, running tests, building the app, and uploading to the App Store or Google Play.
A minimal Android lane looks like this:
lane :android_deploy do
gradle(task: 'bundle', build_type: 'Release')
upload_to_play_store(track: 'internal')
end
And for iOS:
lane :ios_deploy do
match(type: 'appstore')
build_app(scheme: 'YourApp')
upload_to_app_store
end
Customize these based on your signing setup, test requirements, and target release track.
Step 2 — Use Remote Config for Update Checks
Configure the parameter in Firebase
In the Firebase Console, go to Remote Config and create a new parameter named minimum_required_version. Set its default value to the minimum version string you want to enforce, for example 2.0.0.
When you want to raise the minimum — after deprecating an old API, fixing a critical bug, or shipping a breaking change — update this value in the console and publish. No new app build required.
Fetch and check the version in your app
Install the required packages if you have not already:
npm install @react-native-firebase/app @react-native-firebase/remote-config react-native-device-info
cd ios && pod install
Then add the version check to your app entry point:
import { useEffect } from 'react';
import { Alert, Linking } from 'react-native';
import DeviceInfo from 'react-native-device-info';
import remoteConfig from '@react-native-firebase/remote-config';
const useUpdateCheck = () => {
useEffect(() => {
async function checkForUpdate() {
await remoteConfig().fetchAndActivate();
const minimumRequiredVersion = remoteConfig()
.getValue('minimum_required_version')
.asString();
const currentVersion = DeviceInfo.getVersion();
if (compareVersions(currentVersion, minimumRequiredVersion) < 0) {
Alert.alert(
'Update Required',
'A new version of the app is available. Please update to continue.',
[
{
text: 'Update Now',
onPress: () => {
// Android
Linking.openURL('market://details?id=com.yourapp.package');
// iOS — replace with your App Store URL
// Linking.openURL('itms-apps://itunes.apple.com/app/idYOURAPPID');
},
},
],
{ cancelable: false }
);
}
}
checkForUpdate();
}, []);
};
export default useUpdateCheck;
Call useUpdateCheck() from your root component. The { cancelable: false } option on the alert prevents users from dismissing the prompt without updating.
You will need a compareVersions utility that parses semver strings and returns a negative number if the first argument is older than the second. A simple implementation:
const compareVersions = (a, b) => {
const pa = a.split('.').map(Number);
const pb = b.split('.').map(Number);
for (let i = 0; i < 3; i++) {
if ((pa[i] || 0) < (pb[i] || 0)) return -1;
if ((pa[i] || 0) > (pb[i] || 0)) return 1;
}
return 0;
};
Step 3 — GitHub Actions Workflow
Create the workflow file
Add a workflow at .github/workflows/deploy.yml:
name: Deploy React Native App
on:
push:
branches:
- main
jobs:
deploy-android:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: '18'
- name: Install dependencies
run: npm install
- name: Set up Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: '3.1'
bundler-cache: true
- name: Deploy Android
run: bundle exec fastlane android_deploy
env:
FASTLANE_JSON_KEY_DATA: ${{ secrets.FASTLANE_JSON_KEY_DATA }}
deploy-ios:
runs-on: macos-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: '18'
- name: Install dependencies
run: npm install && cd ios && pod install
- name: Set up Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: '3.1'
bundler-cache: true
- name: Deploy iOS
run: bundle exec fastlane ios_deploy
env:
FASTLANE_USER: ${{ secrets.FASTLANE_USER }}
MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
APP_STORE_CONNECT_API_KEY: ${{ secrets.APP_STORE_CONNECT_API_KEY }}
Add GitHub Secrets
In your repository, go to Settings > Secrets and variables > Actions and add the secrets referenced in the workflow:
| Secret | What it holds |
|---|---|
FASTLANE_JSON_KEY_DATA |
Google Play service account JSON key |
FASTLANE_USER |
Apple ID used for App Store Connect |
MATCH_PASSWORD |
Password for your Fastlane Match certificate repo |
APP_STORE_CONNECT_API_KEY |
App Store Connect API key JSON |
Push changes to the main branch to trigger the workflow.
How It All Connects
Once this is in place, the release flow works like this:
- Push to
main— GitHub Actions builds and deploys the app via Fastlane - The new build ships to users through the App Store or Google Play
- When you need to force an update, raise
minimum_required_versionin Firebase Remote Config and publish - On next app launch, users running an older version see the mandatory update prompt
No code change or new release is needed to adjust the minimum version threshold — that control lives entirely in Firebase.
Notes
- Always test the
compareVersionslogic with edge cases — versions like1.10.0vs1.9.0trip up naive string comparison. - Consider adding a
fetchAndActivatetimeout viaremoteConfig().setConfigSettings({ fetchTimeMillis: 3000 })to avoid blocking app startup if Firebase is slow to respond. - For iOS, make sure the
Linking.openURLtarget uses the correctitms-apps://scheme with your actual App Store app ID.
Top comments (0)