Skip to content
Colin Wren

Seamlessly Deploying React Native Apps with Expo and Github Actions

DevOps, Automation, JavaScript, Software Development4 min read

Github action CI build result
This is far easier than asking someone to pull an old Mac out of their cupboard to build your iOS app

In May 2019 I started volunteering my time to a community organisation for a game I play. The organisation had published a few apps to help the player base get the most out of the game, and as someone who organises community events, I used the app a lot to track attendance and issue rewards.

When I first picked up the code base, my initial drive was to get the code documented and tested as this would make development easier, but I hadn’t realised the real issue with the app — the inability to release often and quickly.

While improvements to the app could be made, the release process was less than ideal and this meant that shipping updates to the users took two weeks and often left people frustrated, lessening the value of these updates.

The release process involved:

  • Getting hold of the person who originally created the signing certs
  • That person has some time to do the release
  • That person has to locate the Mac they used to build the app (kept in a cupboard somewhere)
  • That person ultimately having to re-image the Mac, install Xcode and then finally testing the app with the new setup
  • Finally the app would be built and published to Testflight

As builds on Testflight are good for 90 days it was not uncommon for these builds to expire before the app hit the App Store and the whole process would start again.

A New Hope

In December 2019 I was asked by the organisation to build a brand new app for a different cohort of the player base. I wanted to improve on this process and made sure the first thing I did was ensure we could ship the app automatically.

I’d been using Github Actions for the CI on the previous apps so I decided to port over my CI scripts and use Github Actions to handle the deployment of the app on the tagging of a release in Github.

Additionally I had worked with Expo when building a component library for the previous app, and I really enjoyed the development experience it offered so I decided to look into the publishing tools that Expo offered and was very impressed.

Expo offers a build service for free that once set up will generally build and publish the iOS version of the app within 20 minutes. Android I have found takes about 30 minutes to build but for the price of nothing and to do so at the press of a button is so much better than the previous deployment setup!

Setting up deployments in Expo

Before you get started make sure you sign up for an Expo account as this will be needed for the CLI steps.

Once you have your account you’ll then need to grab the Expo CLI tool, you can grab this via npm install -g expo-cli .

In order to build your app, you’ll need to provide Expo with a number of files that assist with the signing and uploading of the app to Apple and Google.

A detailed walkthrough on what you need to do can be found on the Expo documentation site but here’s a summary of the different actions and files you’ll need for iOS:

  • Add an entry in App Store Connect for the app (use the bundleIdentifier fromapp.json )
  • Create an iOS distribution cert and export the private key as a .p12 file via Keychain
  • Create a .p8 file if you’re using push notifications (I found this article useful for this)
  • Create a provisioning profile for the app

Once you have all the relevant files you can then use the Expo CLI to build the app via expo build:ios which will prompt you to upload these files to Expo.

Expo stores these files securely and only accesses them to build your app, removing them from the build runner when done.

You can also ensure that publishing the app to Testflight works via expo upload:ios , consider this a test run before we start automating that operation via Github Actions.

It’s important to carry out a test run as there will be a bunch of stuff that Apple will throw warnings in the App Store Connect UI on your first upload.

One of the things that I encountered was the need to sign an export compliance agreement on ever release, which is less than ideal for an automated build, however setting ios.config.usesNonExemptEncryption to false in app.json sorted this out.

Setting up automated deployments in Github Actions

Once you’re happy with your ability to upload the app to Testflight Expo the next step is to automate it all!

One of the benefits of the Github Actions platforms is that you can use actions created by others in your scripts to speed up development and stay up to date with any changes.

Expo publishes one of these actions called expo/expo-github-actions@4 which when you provide the appropriate environment variables via the Github Secrets store will log you in and set up the Expo CLI ready for use.

You will also need to set the EXPO_APPLE_ID and EXPO_APPLE_ID_PASSWORD environment variables in order to publish to Testflight, with these set you can call expo upload:ios without any other input.

Here’s an example of how to run expo build:ios and expo upload:ios on a release being tagged:

1name: Publish to Testflight
4 release:
5 types: [created]
8 publish-to-testflight:
9 runs-on: macos-latest
10 steps:
11 - uses: actions/checkout@v1
12 - uses: actions/setup-node@v1
13 with:
14 node-version: 10
15 - uses: expo/expo-github-action@v4
16 with:
17 expo-username: ${{secrets.EXPO_CLI_USERNAME}}
18 expo-password: ${{secrets.EXPO_CLI_PASSWORD}}
19 expo-packager: yarn
20 - name: Install deps
21 run: yarn install
22 - name: Build iOS app
23 run: expo build:ios
24 env:
25 EXPO_APPLE_ID: ${{secrets.EXPO_APPLE_ID}}
27 - name: Upload iOS app
28 run: expo upload:ios --app-name "[SET THIS TO YOUR APP NAME]"
29 env:
30 EXPO_APPLE_ID: ${{secrets.EXPO_APPLE_ID}}
Publish to Testflight on creating a release on Github

I’ve only had the job fail once which was due to an issue with Expo deciding to retire a set of certificates used when communicating with its services, they did the update but the Expo CLI wasn’t updated, they fixed that within a day though.

The proof is in the pudding

By automating the build and deployment of the app I’ve removed the bottleneck of unmanaged tooling and human intervention and this has meant that on cutting a release the Testflight testers can have a new build in their hands within an hour.

However, it’s even quicker to get simple code changes into the development team’s hands as Expo offers over-the-air (OTA) updates of the JS bundle via the expo publish command which runs on every push to master branch with a separate channel for the development team.

There are plans to open up this OTA update functionality to the users too which would see even quicker updates and a much quicker feedback loop.

There’s also been a cultural impact to the team now the releases are a trivial exercise, they feel empowered to make changes and the product owner is less concerned with grouping functionality into big ‘releases’ as now things can be delivered over many small releases and then turned on via config.

It’s really refreshing to work on a project where I can have the final product in my hands almost immediately.