Automating Deployments for Cross Platform Mobile Apps

Yash Mestry & Samarth Shetty
Engineering Team
Read Time
10 min read
Published On
November 14, 2024

Mobile app deployment is a crucial aspect of the development process. Automating this step can significantly enhance efficiency and control over an application’s build to deploy lifecycle. This article provides deployment options for a React Native app, identifies the best strategies, and discusses how to automate them. By integrating these approaches, we achieve the ability to deliver mobile app updates with the same speed as web app updates.

Both Google Play Store and Apple’s App Store have distinct criteria for publishing apps, making consistent cross-platform deployment automation challenging. And to keep up with the speed of agile development, there is clear need for streamlined, time-efficient deployment workflows.

Challenges of Manual Deployment

Early in our mobile app development journey, we used Expo’s free tier for building and deploying apps. Although we could build the app on Expo and manually upload it to iOS and Android testing environments, this method introduced unpredictable wait times and limited control. Expo’s free tier provides 30 free builds each month (up to 15 for iOS) with EAS Build, but this limit can quickly be exhausted even in a limited sized project. The variability in build times for free plans—ranging from seconds to hours—poses a significant challenge, especially when dealing with high-severity or security issues that require immediate fixes.

While Expo offers a paid version for faster deployments, its added cost may not always fit within project budgets, especially for frequent deployments across multiple environments. Local builds can be an alternative, but they are repetitive and unavailable across development environment platforms (i.e. Mac vs. Windows), further complicating the process. These limitations pushed us to explore more cost-effective, reliable deployment solutions that wouldn’t compromise speed or quality, while keeping project spend in check.

Automating App Deployment: Requirements

To avoid the pitfalls of manual deployments, we identified several key requirements for automating our app deployment:

  • Multiple environments for testing and development.
  • Elimination of unpredictable wait times associated with Expo.
  • Ability to deploy both iOS and Android versions concurrently.
  • Ability to build and deploy apps to testing environments like TestFlight and Google Play Console.
  • Simplified quality assurance (QA) testing processes.

Metrics to Consider:

  • Deployment Time: The average time from starting the deployment process to when the app is live. This includes the unpredictable app review processes, which can delay deployment further.
  • Deployment Frequency: The number of deployments pushed to environments in a given time. If the frequency is less this metric does not matter, but if it's high it can impact delivery.
  • Auto/Manual: Once apps are published on app stores, they become permanent. Consider a scenario where a developer mistakenly publishes an app linked to the staging or development environment instead of production. The additional delay from app review processes can extend the time before the issue is resolved. Therefore, any deployment strategy should be automated to minimize the potential for developer errors.
  • Tech Stack Complexity: Developing an app involves managing many components, such as third-party integrations, backend APIs, and various plugins and libraries. Each of these elements is vital to the app's success, and integrating them correctly into the deployment process is equally critical. Even a minor version mismatch in a library can disrupt the entire deployment workflow and bring development to a standstill.
  • Developer Convenience: A significant challenge in app development is seamlessly integrating the development and operations processes to create a smooth lifecycle. It's crucial to understand the needs of developers and ensure that their workflow is as streamlined and hassle-free as possible. By making deployments straightforward and efficient for developers, the DevOps team can help them concentrate on coding without being bogged down by deployment issues, ultimately optimizing the deployment process.
  • Budget of Project: Who wouldn’t value a deployment solution that is both cost-efficient and reduces overhead? An optimized deployment strategy not only helps to manage and lower server costs but also simplifies the client’s experience by reducing the complexity of deployment management. By carefully balancing cost and efficiency, such a solution can minimize unnecessary expenses and streamline the entire deployment process. This approach not only alleviates financial strain but also ensures a smoother and more straightforward experience for clients, making it a highly effective and attractive option for managing deployments.

Tech Stack:

To address these deployment challenges, we implemented the following technologies:

  • GitHub Repositories for code and version control
  • Expo Framework for front-end development
  • Expo and EAS for builds
  • Fastlane library for cross-platform deployments
  • TestFlight for iOS and Google Play Console for Android app testing

For deciding the deployment workflow, our team wanted to make it very easy to access for both developers and testers, and using Expo was a straightforward answer to that requirement. There are alternatives like React Native CLI, a command line interface (CLI) tool created for the react-native package specifically.

The key difference between Expo and React Native CLI lies in their approach to the process of mobile app development. Expo offers a set of robust defaults and simplifies many aspects of app development, such as app permissions, push notifications, and dark mode. This allows developers to concentrate on crafting excellent user experiences without worrying about conflicts between third-party libraries, integration issues, upgrades, and maintenance.

On the other hand, React Native CLI provides more flexibility in selecting third-party libraries, which can be beneficial since Expo might limit certain app capabilities. However, this flexibility comes at the expense of additional developer time spent on integrating these libraries with native platforms. Consequently, while Expo’s native container is more constrained, it ensures stability across releases.

There are also some drawbacks to using Expo, such as:

  • Native Modules, which are exclusive to React Native CLI, allow JavaScript code in React Native to communicate with native platform code.
  • The latest updates for iOS and Android platforms aren't immediately available with Expo; there's a delay until the Expo team incorporates them into a future release.
  • React Native CLI-based apps have almost limitless capabilities.

Expo vs. React Native CLI

When deciding on our deployment workflow, we initially found that Expo was more accessible for our developers. Expo simplifies many aspects of mobile development—app permissions, push notifications, and dark mode—allowing developers to focus on creating great user experiences without dealing with complex integrations. However, it does have its limitations.

Expo’s closed ecosystem, while stable, delays access to the latest iOS and Android updates. To address the need for more customization while retaining Expo’s benefits, we opted for Expo’s prebuild feature. This approach allows us to incorporate native customizations, similar to what’s possible with React Native CLI, but still within the Expo environment. While the React Native CLI offers extensive flexibility, Expo’s prebuild strikes a balance, enabling us to integrate native modules without requiring a full migration, making it a practical choice for high-quality app development.

Automating Deployment with Fastlane

One of the most valuable tools we use for automating app deployment is Fastlane, a widely adopted tool written in Ruby. Fastlane simplifies deployments across multiple environments (e.g., development, staging, production) by creating dedicated lanes for each platform. The Fastfile contains the deployment commands, and an auto-generated README.md helps all team members manage deployments consistently.

Fastlane integrates seamlessly with third-party services and allows us to automate app builds and submissions to the iOS App Store and Google Play Console, freeing up developers to focus on core tasks.

Leveraging GitHub Actions for CI/CD

GitHub Actions provides an efficient platform for automating continuous integration and deployment (CI/CD) pipelines. Using YAML files, teams can define workflows for building, testing, and deploying applications. For React Native, we configured GitHub Actions to automate local builds for both iOS and Android environments.

By triggering workflows on code pushes or pull requests, our team can automate app builds and deployments without relying on external build services, ensuring faster and more flexible development cycles. This approach allows for more control over deployment timelines and mitigates dependency on cloud-based Expo services.

Git Branching and Versioning

To manage deployment across multiple environments (development, staging, production), we implemented a strict Git branching strategy to prevent unintentional deployment of non-production builds. Each environment connects to its corresponding backend, so it's critical that only stable, production-ready builds are pushed to live environments.

We also adhere to Semantic Versioning (SemVer) to track app evolution, which uses a MAJOR.MINOR.PATCH structure:

  • MAJOR version increases when you make incompatible API changes or introduce breaking changes.
  • MINOR version increases when you add backward-compatible functionality.
  • PATCH version increases when making backward-compatible bug fixes or minor updates.

For example, a build version like 1.2.0 indicates the first major release with two minor feature updates. A subsequent bug fix would bump this to 1.2.1.

When deploying across different environments:

  • Dev Environment builds could use a pre-release identifier (e.g., 1.2.0-dev.1), which distinguishes these builds from production versions.
  • Staging Environment builds could be tagged similarly (e.g., 1.2.0-staging.1).
  • Production Environment builds would have a clean version like 1.2.0.

By enforcing this versioning scheme, it’s easier to prevent accidental deployment of dev or staging builds to production environments. You can integrate version checks within CI/CD pipelines to further ensure that only production versions are pushed to app stores. Additionally, versioning helps rollback if an issue is discovered in a live environment, providing a clear history of the application’s changes.

TestFlight & Google Play Store Testing Tracks

In mobile app development, thorough testing of pre-release versions across both iOS and Android platforms is crucial to ensure a smooth user experience before the official launch. Managing beta testing, collecting feedback, and coordinating builds for various environments—such as development, staging, and production—can be challenging without the right tools. To streamline this process, we utilized Apple’s TestFlight for iOS and Google Play Console’s testing tracks for Android, allowing the team to focus on quality testing and feedback, rather than the complexities of distribution logistics.

TestFlight simplifies iOS beta testing by enabling developers to upload builds directly from Xcode to App Store Connect and distribute them to internal and external testers via email or public links. It also facilitates easy feedback collection, with testers able to submit crash logs, screenshots, and detailed reports. Multiple builds can be distributed simultaneously across development and staging environments, ensuring the isolation of non-production code from stable releases. TestFlight also automates the update process, allowing testers to always have the latest version with minimal manual involvement.

Similarly, Google Play Console’s internal, closed, and open testing tracks offer a robust solution for managing Android beta releases. Developers can upload APKs or AABs to specific testing tracks, inviting testers through email or public links, and gathering valuable feedback and crash reports. The flexibility to manage multiple testing tracks allows us to test different builds in isolation, ensuring that only stable, production-ready versions are promoted to the live store. Automatic updates keep testers on the most recent build, streamlining the feedback loop.

Automating Pipelines with GitHub Actions

For Github Actions our Android and iOS pipelines will run the following steps:

1. Manual Triggers:

Both pipelines allow for manual triggering through GitHub Actions, where users input version details such as new_version and, in the case of Android, also version_code. This ensures version control right from the start of the process.

2. Environment Setup:

The pipelines begin by setting up the necessary environment:

  • Android: Installs Node.js, Java JDK, and configures AWS credentials to download essential files such as keystores and configuration files from an S3 bucket.
  • iOS: Installs Node.js, Java JDK, and prepares macOS for iOS-specific builds using Xcode and Apple credentials.
3. App Configuration Updates:

Configuration files (e.g., appConfig.ts, app.json) are updated in both pipelines to point to the appropriate environment (staging or production):

  • API base URLs, tokens, and environment-specific settings are adjusted dynamically.
  • For iOS, the bundleIdentifier, scheme, and project_id are updated accordingly.
4. Prebuild and Versioning
  • Android: Uses Expo to pre-build the app, updating version details in build.gradle and app.json as per the provided inputs.
  • iOS: Ensures the correct versioning and project details are in app.json and initializes the iOS app for building through Expo and EAS.
5. Build Process
  • Android: Builds the APK and Android App Bundle (AAB) using Gradle for Android.
  • iOS: Utilizes Xcode to generate an IPA file for iOS, ensuring the app is packaged correctly for distribution.
6. Artifact Upload

Both pipelines upload the generated app artifacts (APK, AAB, or IPA) to GitHub for future access:

  • Android: Uploads APK and AAB files.
  • iOS: Uploads the IPA file.
7. Store/Testing Deployment
  • Android: Publishes the build to the Google Play Store’s internal track for testing.
  • iOS: Uploads the IPA file to TestFlight for distribution and testing.
8. Notifications and Version Control

After the build and deployment processes are complete:

  • Both pipelines send a notification to a Slack channel, informing the team about the successful build and where the artifacts can be accessed.
  • In the Android pipeline, version changes are committed to the GitHub repository to ensure consistency in source control.

This structure ensures a consistent and automated CI/CD workflow for both Android and iOS platforms, improving the efficiency of the development and release process while minimizing manual errors.

Benefits of the Automated Process

Once the pipelines do their magic and the QA team receives the app they are free to carry out tests as required before they release the app to the stores.

  • Faster Deployments: We observed a 30% increase in deployment speeds, significantly enhancing our deployment schedule and reducing time-to-market. This efficiency allows us to roll out new features, bug fixes, and updates more quickly, providing a competitive edge in a fast-paced market.
  • Fail Fast Strategy: Automation enables us to adopt a "fail fast" approach, where issues are identified, debugged, and resolved almost instantly after deployment. This reduces the impact of errors, allowing faster iterations and ensuring higher quality in subsequent releases.
  • Improved Consistency and Accuracy: Automation eliminates the human error associated with manual processes, ensuring that deployments and tests are executed consistently across different environments. This guarantees a uniform user experience and increases the stability of releases.
  • Continuous Integration and Delivery (CI/CD): Automating the deployment and testing process integrates seamlessly with CI/CD pipelines. This ensures that code changes are continuously tested and deployed in a streamlined manner, reducing bottlenecks and increasing development velocity.
  • Scalability: As the app and its user base grow, automation provides the flexibility to scale testing and deployment processes without the need for additional manual effort. This ensures that quality is maintained even as the app expands in complexity and size.
  • Cost Efficiency: By reducing manual labor and minimizing downtime caused by errors, automation can lower operational costs. Automated testing also means fewer resources are needed to test each release, making it a cost-effective solution in the long run.
  • Faster Feedback Loops: Automation ensures that developers receive real-time feedback on issues or performance problems, enabling faster decision-making. This contributes to a more agile development process and helps prioritize fixes or enhancements based on immediate insights.
  • Enhanced Security: Automated deployment processes can include security checks, such as vulnerability scans and compliance tests, ensuring that the app remains secure throughout the development lifecycle. This helps to detect and mitigate security risks before they reach production.

Automating the deployment and testing process for native mobile apps has proven to be a game-changer in delivering high-quality, fast, and secure updates. By leveraging tools like Expo, GitHub Actions, and Fastlane, we’ve been able to eliminate manual inefficiencies and reduce wait times, allowing us to deploy iOS and Android apps with the same speed and confidence as back-end updates. With a well-structured CI/CD pipeline in place, our team has adopted a "fail fast" strategy, ensuring that any issues are quickly identified and resolved, further enhancing our app's reliability.

Not only does automation improve deployment speeds and testing accuracy, it also ensures consistency across multiple environments, such as development, staging, and production. This leads to a smoother experience for both developers and end-users while reducing operational costs and risks associated with manual errors. Through the integration of multiple environments and proper versioning strategies, we safeguard against accidental deployment of non-production code to live environments.

By adopting these automation strategies, we at Perpetual have optimized our mobile app deployment and testing workflows, resulting in faster time-to-market, greater scalability, and enhanced security. This approach positions us and our customers to stay ahead in the competitive mobile app landscape, delivering high-performance apps with a streamlined and cost-effective deployment process.