Shipping Fast With Continuous Deployment

Automation of the critical pieces of the development lifecycle can help you improve the overall development speed and security. The shorter the feedback loop, the faster your team can iterate on the product itself. However, testing and development are only a part of the activities that you have to perform when working on a product. Another important step is the deployment – building and distributing the application to production. Most of the time, this process is manual.

Building and distributing your apps manually is a complex and time-consuming process.

The deployment takes time to set up and is far more complex than just running tests in the cloud. For example, on iOS, Xcode configures many settings and certificates automatically. This ensures a better developer experience for someone who’s working on a native application. Developers who are used to such an approach often find it challenging to move the deployment to the cloud and set up such things as certificates manually.

The biggest downside of the manual approach is that it takes time and doesn’t scale. In consequence, teams that don’t invest in the improvements to this process end up releasing their software at a slower pace.

A graph showing the continuous deployment process

What is Continuous Deployment (CD)?

Continuous Deployment is a strategy in which software is released frequently through a set of automated scripts. It aims at building, testing, and releasing software with greater speed and frequency. The approach helps reduce the cost, time, and risk of delivering changes by allowing for more incremental updates to applications in production.

With manual deployment, you’re not shipping new features and fixes as quickly as you should.

Building and distributing your application manually slows down your development process regardless of how big your team is. Even in small product teams of around five people, automated build pipelines make everyone’s work easier and reduce unnecessary communication. This is especially important for remote companies.

Continuous Deployment also allows you to introduce standards and best practices focused on improving the overall performance of the application, such as leveraging Hermes to optimize app startup time. With all the steps required for the deployment in a single place, you can ensure that all releases are done the same way and enroll company-wide standards.

Establish a CD setup that makes the build and generates the changelog

When it comes to automating the deployment of mobile applications, there are a few established ways to go.

One way is to write a set of scripts from scratch by interacting with xcode and gradle directly. Unfortunately, there are significant differences between the tooling of Android and iOS, and not many developers have enough experience to handle this automation. On top of that, iOS is much more complicated than Android due to advanced code signing and distribution policies. And as we have said before, if you are doing it manually, even Xcode cannot help you.

https://fastlane.tools/

Another way is to use a pre-existing tool in which the developers have handled the majority of use cases. Our favorite one is fastlane - a set of modular utilities written in Ruby that lets you build your iOS and Android applications by writing a set of instructions in a configuration file.

After you have successfully built your binaries, it is time to deploy them to their destination. Again, you can either upload the files to the desired service (e.g.App Store) manually or use a tool that will take care of that for you. For the same reasons as before, we prefer to use an existing solution – in this case, AppCenter by Microsoft.

Visual Studio App Center logo

AppCenter is a cloud service with tooling for the automation and deployment of your application. Its biggest advantage is that many of the settings can be configured from the graphical interface. It is much easier to set up the App Store and Play Store deployments this way, rather than working with uploads from the command line.

The same can be achieved with EAS by combining EAS Build to build your app bundles and EAS Submit to automatically upload them to your preferred track on the Google Play Store and TestFlight on App Store Connect.

For the purpose of this article, we will use fastlane and AppCenter in CircleCI pipelines to fully automate the process of app delivery to the final users. Then, we will dive into the EAS Submit.

Note: Describing the ins and outs of the setup would make this article too long. That’s why we have chosen to refer only to the specific documentation. Our goal is to provide you with an overview, and not a step-by-step guide since the final config will be different for each project.

Setting up fastlane

Before going into the details for Android and iOS, you have to make sure that the fastlane has been installed and configured on our devices.

Next, you have to run the init command within the React Native project. We will run the fastlane command twice, from each native folder. This is because React Native is two separate apps at a low level.

As a result, this command will generate setup files in both ios and android folders. The main file in each folder would be called Fastfile and it’s where all the lanes will be configured.

> cd ./ios && fastlane init
> cd ./android && fastlane init
view raw gistfile1.txt hosted with ❤ by GitHub

In the fastlane nomenclature, a lane is just like a workflow - a piece that groups low-level operations deploying your application.

Low-level operations can be performed by calling actions – predefined fastlane operations that simplify your workflow.

Setting up fastlane on Android

Now that you have successfully set up fastlane in our projects, you are ready to automate the deployment of our Android application. To do so, you can choose an Android-specific action - in this case gradle. As the name suggests, Gradle is an action that allows you to achieve similar results as with Android Gradle used standalone.

Our lane uses the gradle action to first clean the build folder, and then assemble the APK with signature based on passed params.

default_platform(:android)
project_dir = File.expand_path("..", Dir.pwd)
platform :android do
lane :build do |options|
if (ENV['ANDROID_KEYSTORE_PASSWORD'] && ENV['ANDROID_KEY_PASSWORD'])
properties = {
"RELEASE_STORE_PASSWORD" => ENV['ANDROID_KEYSTORE_PASSWORD'],
"RELEASE_KEY_PASSWORD" => ENV['ANDROID_KEY_PASSWORD']
}
end
gradle(
task: "clean",
project_dir: project_dir,
properties: properties,
print_command: false
)
gradle(
task: "assemble",
build_type: "Release",
project_dir: project_dir,
properties: properties,
print_command: false
)
end
end

You should be able to run a lane build by implementing:

$ cd ./android && fastlane build
view raw gistfile1.txt hosted with ❤ by GitHub

This should produce a signed Android APK.

Note: Don’t forget to set environment variables to access the keystore. These are RELEASE_STORE_PASSWORD and RELEASE_KEY_PASSWORD; they have been set in the example presented above.

Setting up fastlane on iOS

With the Android build being automated, you’re ready to move to iOS now. As we discussed earlier, iOS is a bit more complex due to the certification and provisioning profiles. They were designed by Apple to increase security. Fortunately, fastlane ships with a few dedicated actions that help us overcome these complexities.

You can start with the match action. It helps in managing and distributing iOS certificates and provisioning profiles among your team members. You can read about the idea behind match in the code signing guide concept. Simply put, match takes care of setting up your device in a way that it can successfully build an application that will be validated and accepted by the Apple servers.

Note: Before you move any further, make sure that your init matches your project. It will generate the required certificates and store them in a central repository where your team and other automation tools can fetch them.

Another action that you could use apart from match is gym. Gym is similar to Gradle action in a way that it actually performs the build of your application. To do so, it uses the previously fetched certificates and signs settings from match.

default_platform(:ios)
ios_directory = File.expand_path("..", Dir.pwd)
base_path = File.expand_path("..", ios_directory)
ios_workspace_path = "#{ios_directory}/YOUR_WORKSPACE.xcworkspace"
ios_output_dir = File.expand_path('./output', base_path)
ios_app_id = 'com.example'
ios_app_scheme = 'MyScheme'
before_all do
if is_ci? && FastlaneCore::Helper.mac?
setup_circle_ci
end
end
platform :ios do
lane :build do |options|
match(
type: options[:type],
readonly: true,
app_identifier: ios_app_id,
)
cocoapods(podfile: ios_directory)
gym(
configuration: "Release",
scheme: ios_app_scheme,
export_method: "ad-hoc",
workspace: ios_workspace_path,
output_directory: ios_output_dir,
clean: true,
xcargs: "-UseModernBuildSystem=NO"
)
end
end

You should be able to run lane build by running the same command as for Android:

$ cd ./ios && fastlane build
view raw gistfile1.txt hosted with ❤ by GitHub

This should produce an iOS application now too.

Deploying the binaries with App Center

Now that you have automated the build, you are able to automate the last part of the process - the deployment itself. To do so, you could use App Center.

Note: You have to create an account in the App Center, apps for Android and iOS in the dashboard, and generate access tokens for each one of them. You will also need a special Fastlane plugin that brings an appropriate action to your toolbelt. To do so, run `fastlane add_plugin appcenter.`

Once you are done with configuring your projects, you are ready to proceed with writing the lane that will package the produced binaries and upload them to the App Center.

lane :deploy do
build
appcenter_upload(
api_token: ENV[‘APPCENTER_TOKEN’],
owner_name: “ORGANIZATION_OR_USER_NAME”,
owner_type: “organization”, # ‘user’ | ‘organization’
app_name: “YOUR_APP_NAME”,
file: “#{ios_output_dir}/YOUR_WORKSPACE.ipa”,
notify_testers: true
)
end

That’s it! Now it is time to deploy the app by executing deploy lane from your local machine.

Integrating your pipeline with CircleCI

Using all these commands, you are able to build and distribute the app locally. Now, you can configure your CI server so it does the same on every commit to main. To do so, you will use CircleCI.

Running fastlane on CI server usually requires some additional setup. Refer to the official documentation to better understand the difference between settings in local and CI environments.

To deploy an application from CircleCI, you can configure a dedicated workflow that will focus on building and deploying the application. It will contain a single job called deploy_ios, which will execute our fastlane command.

version: 2.1
jobs:
deploy_ios:
macos:
xcode: 14.2.0
working_directory: ~/CI-CD
steps:
- checkout
- attach_workspace:
at: ~/CI-CD
- run: npm install
- run: bundle install
- run: cd ios && bundle exec fastlane deploy
workflows:
deploy:
jobs:
- deploy_ios

Pipeline for the Android will look quite similar. The main difference would be the executor. Instead of a macOS one, a docker react-native-android Docker image should be used.

Note: This is just a sample usage within CircleCI. In your case, it may make more sense to define filters and dependencies on other jobs to ensure the deploy_ios is run at the right point in time.

You can modify or parametrize the presented lanes to use them for other kinds of deploys, for instance for the platform-specific App Store. To learn the details of such advanced use cases, get familiar with the official fastlane documentation.

EAS Submit

EAS Submit is a hosted service for uploading and submitting your app binaries to the app stores. Unlike with CircleCI or AppCenteryou don’t need to go through creating your app signing credentials and signing your builds manually. The EAS CLI eases you through this process and can do it automatically when you run eas build.

If needed, you can also manage your signing credentials without creating a build using eas credentials.

In order to use EAS Submit, you will first need to run eas build to create the production .ipa for Apple and .aab for Android.

Uploading iOS apps to App Store Connect with EAS Submit

Once you have your build .ipa either on your local machine or EAS, open your terminal and run eas submit . The cli will ask you to either choose a build from EAS or from your local machine, you’ll be prompted to log into your Apple Developer account, and the build will be uploaded to App Store Connect. It usually takes a couple of minutes for it to finish processing and become available on App Store Connect.

Alternatively, you can build and submit your app in one command with

eas build –auto-submit

Using eas submit to upload your app will not make it immediately available on the Apple App Store. It is not possible to upload to the App Store directly. Instead, eas submit will upload your app to TestFlight from which you can choose to either publish it to a test group on TestFlight, or create a release and submit it for App Store review. Only after the app has passed review can it be made available to users on the App Store.

Uploading Android apps to Google Play with EAS Submit

Before you can use eas submit to automatically upload builds to Google Play, some additional configuration is required. First you will need to create your Android app on the Google Play console and upload the first build manually. For this, you can use eas build to create the build, download it from EAS, and drag and drop the .aab file to the app upload section on Google Play Console.

During this process, you’ll have to fill in all the metadata about your app including adding app screenshots, marketing descriptions, terms and conditions, and security and privacy declarations. If you open Dashboard on your Google Play Console, make sure all the items under ''Finish setting up your app'' are checked off. Then open ''Publishing Overview'' and ensure all changes have been submitted for approval and approved.

Once that’s done, you’ll need to set up a Google Service account. After completing the guide, you should have downloaded the JSON private key for your Google Service account (this is a private key so it should be stored securely and not committed to .git). Add a path to the JSON file under serviceAccountPath in your eas.json:

{
''submit'': {
''production'': {
''android'': {
''serviceAccountPath'': ''../path/to/api-xyz.json'',
''track'':''internal''
}
}
}
}

Now you’re all set up to do an automatic submission! For the next build you want to upload, you can run eas submit to submit it automatically, or run eas build –auto-submit to build and submit it in one go.

Google Play builds are uploaded to specific test tracks with ''internal'' being the lowest. You can upload to a different test track or manually promote the release up from Google Play as it passes each stage of testing.

How Continuous Deployment benefits your business

With automated deployment, you no longer waste your time on manual builds and sending the artifacts to test devices or app stores. Your stakeholders are able to verify features faster and shorten the feedback loop even further. With regular builds, you will be able to catch or ship fixes to any critical bugs with ease. If your app could use a performance boost, check out our React Native development services and give us a shout.

Insights

Learn more about CI/CD

cover

Aug 21Article

Automating React Native Evals With Airflow

How Airflow coordinates React Native Evals, runs solver and judge models separately, and keeps every result traceable through human review.

cover

Jun 19Article

Caching React Native Builds on S3 and R2 With Rock

Learn how to cache your React Native builds and store them on Amazon S3 or R2 to reuse across your team.

cover

Oct 29Podcast

How to Handle App Center Retirement

Discover App Center alternatives for React Native. Explore migration tips and Expo's role in the transition from CodePush & other App Center functionalities.

cover

Aug 4Article

Mastering Fastlane: Overcoming Challenges in Passing Build Properties to Native Layers

Master passing build properties with Fastlane for efficient application development.

cover

May 14Article

Ship OTA (Over-The-Air) When in an Emergency

Shipping Over-The-Air may save your app from a lot of trouble. Find out why and how you can make your application OTA-ready.

cover

Dec 13Article

How Can Continuous Integration (CI) Improve Your React Native Apps?

Read the article and find out how Continuous Integration (CI) can improve the building, testing, and distribution of your React Native apps.

cover

Apr 24Article

Continuous Delivery - Integration for Everyone

A quick guide on how to tame Continuous Delivery and Integration. Dive into a real-world scenario from a Callstack client project.

cover

Mar 19Tutorial

The Hard Way vs. The React Native AI SDK Way: Stop Writing Custom Modules for Every Model

Learn how React Native AI uses a unified provider model to switch between cloud and on-device LLMs without rewriting application logic, enabling reliable AI experiences online and offline.

cover

Mar 12Tutorial

Build Smarter Apps: Tool Calling & AI Orchestration Explained

Learn how on-device models in React Native can call tools, interact with external APIs, and return structured outputs you can use directly in application logic.

cover

Mar 5Tutorial

MLC LLM + React Native: On-Device AI Without the Pain

Learn how to run third-party on-device LLMs in React Native using MLC LLM. Choose your models, run them efficiently across platforms, and keep a unified JavaScript API with React Native AI.

cover

Feb 24Tutorial

Intelligent Fallbacks with Apple Intelligence in React Native

Learn how to use Apple’s built-in Foundation Models in React Native apps, reduce memory usage, and enable fast, fully on-device AI with React Native AI Apple.

cover

Feb 10Tutorial

What Is the React Native AI SDK? A Complete Intro & Quickstart

Learn why on-device AI matters for React Native apps, how local LLMs behave offline, and what problems React Native AI is designed to solve from day one.

cover

Jan 29Tutorial

Implementing an Android TurboModule with Kotlin

Learn how to implement a React Native TurboModule on Android, from the TypeScript spec and Codegen to the Kotlin implementation and package registration.

cover

Jan 19Tutorial

Working with Different Threads in Swift TurboModules

Learn how JavaScript, native module, background, and main threads interact in React Native TurboModules, and how to use GCD queues to run async work safely in Swift.

cover

Jan 9Tutorial

How to Add Type-Safe Constants to Swift TurboModules

Learn how to expose typed, immutable constants from a Swift TurboModule using Codegen, and get full end-to-end type safety between native and JavaScript.

cover

Dec 30Tutorial

Adding Event Emitters to Your TurboModule in Swift

Learn how to emit native events from a Swift TurboModule and subscribe to them in JavaScript, including proper memory management and Codegen integration.

Writing a Swift TurboModule in React Native

Dec 22Tutorial

Writing Your First TurboModule in Swift

Learn how to build a fully working TurboModule in Swift, integrate it with Codegen, and bridge it through Objective‑C inside an Expo app.