Automate your development process
Codemagic is now building more than just Flutter apps, meaning that anyone with an existing React Native app can use Codemagic to run their builds. But how do you get started with something like that? Lewis Cianci checks it out.
When it comes to CI/CD, Codemagic has a lot of exciting things to offer to a developer (like a easy project setup, a really great Slack community, and remote access to the build boxes). But now, we’ve also got workflows on Codemagic for React Native — and this is amazing.
Top reasons to use Codemagic CI/CD for React Native apps
Easy project setup
Getting started with building React Native apps is easy thanks to ready-made configuration templates. Simply select React Native App as the starter workflow and kick off your project.
Customizable workflows for iOS and Android
Codemagic was built with cross-platform app development in mind. Have full control over your React Native pipeline with workflows for iOS and Android that fit the needs of your development team.
Seamless code signing for iOS and Android
Simply upload your Android keystore and iOS code signing files to receive signed artifacts. With automatic code signing for iOS, Codemagic can take care of creating and managing signing certificates and provisioning profiles for you.
Unit and integration tests
Assure the quality of your React Native apps with automated tests. Run unit tests with Jest or test the full app on the simulator/emulator or real devices with Appium or Detox.
Distribute React Native apps with ease
Distribute the latest version of your app to testers on TestFlight, Google Play or Firebase App Distribution, or configure your workflows to automatically publish to the Apple App Store or Google Play.
First-class support
Codemagic Slack workspace community has more than 3,000 members. Find quick solutions and discuss your projects with fellow developers.
Getting started with Codemagic CI/CD for React Native apps
After signing up, you can use React Native workflows by creating a new project in Codemagic and simply selecting the React Native App from the options.

Clicking on it will open up the “Getting started guide” for React Native:

Hitting Next shows us our codemagic.yaml
file.

We can download this and add it to our local repository. This configures our Codemagic pipelines, so Codemagic knows how to run our React Native build on its build servers.
Modifying the YAML file for our build
The next step in this process is to modify our codemagic.yaml
file to get it to build our solution, so let’s see what this file looks like:
# Check out https://docs.codemagic.io/getting-started/building-a-react-native-app/ for more information
# Please review and update values in curly braces
workflows:
react-native:
name: React Native App
environment:
vars:
XCODE_WORKSPACE: '{{ ADD WORKSPACE NAME HERE }}'
XCODE_SCHEME: '{{ ADD SCHEME NAME HERE }}'
node: latest
scripts:
- npm install
- echo "sdk.dir=$HOME/programs/android-sdk-macosx" > "$FCI_BUILD_DIR/android/local.properties"
- |
# build Android
cd android
./gradlew assembleDebug
- |
# build iOS
cd ios
pod install
xcodebuild build -workspace "$XCODE_WORKSPACE.xcworkspace" -scheme "$XCODE_SCHEME" CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO
artifacts:
- android/app/build/outputs/**/**/*.apk
- $HOME/Library/Developer/Xcode/DerivedData/**/Build/**/*.app
Clearly, there are some changes that we need to make to this YAML to get a successful build.
The required changes
Android
Broadly speaking, you almost never want to build a debug version of your app in your CI/CD pipeline. We need to tell the build process to build a release build instead. To do this, we just update the lines under #build Android
to look more like this:
- |
# build Android
cd android
./gradlew assembleRelease
The build process will now follow the path as defined in our build.gradle
. Code signing is a topic in itself (covered in depth here), so we’ll just describe the necessary steps to get the build to work.
In your App level build.gradle
(that’s under YourProjectDirectory\android\app
), we need to add in our release signing details. But we need to use the release signing details only when our build is being run on Codemagic, and not locally.
We then configure the build process to read the signing details from the release file.
signingConfigs {
debug {
storeFile file('debug.keystore')
storePassword 'android'
keyAlias 'androiddebugkey'
keyPassword 'android'
}
release {
if (System.getenv()['CI']) { // CI=true is exported by Codemagic
storeFile file(System.getenv()['FCI_BUILD_DIR'] + '/keystore.jks')
storePassword System.getenv()['FCI_KEYSTORE_PASSWORD']
keyAlias System.getenv()['FCI_KEY_ALIAS']
keyPassword System.getenv()['FCI_KEY_PASSWORD']
}
else {
def keystorePropertiesFile = rootProject.file('key.properties')
def keystoreProperties = new Properties()
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
keyAlias keystoreProperties['keyAlias']
keyPassword keystoreProperties['keyPassword']
storeFile file(keystoreProperties['storeFile'])
storePassword keystoreProperties['storePassword']
}
}
}
What’s actually happening here? In a nutshell, if we are running in a release build, we check if the CI environment variable is declared (Codemagic build machines declare this variable). If we are, then we fetch the location of the keystore and appropriate signing details from the environment variables. Otherwise, we use the local key.properties
for these details, and fetch the details from there instead. This means that our builds continue to run not only locally but also on Codemagic, which is a nice bonus.
Our local debug key.properties
might look something like this (this is for a debug keystore, so yours will be different):
storePassword=android
keyPassword=yourpassword
keyAlias=androiddebugkey
storeFile=keystore.keystore
These are just the keys for the debug keystore, but normally, these are incredibly sensitive details. We don’t want just anyone to be able to read these settings, so checking these files into source control is a really bad idea. Instead, we will encrypt them in Codemagic and have the build process decrypt the required details just before the build runs.
Encrypting our build time dependencies
On our project configuration page, we can use the Encrypt environment variables button to convert sensitive details into a format that can be securely checked in.


Next, we can drag our keystore file into this window and receive an encrypted variable containing our keystore. We just click the little clipboard next to the icon to copy the encrypted keystore variable to our clipboard.

We can also use this screen to encrypt individual variables, like the keystore alias and password. We’ll do this now, and paste these values into our codemagic.yaml
file. When we’re done, it should look a little bit like this:

We’ve configured our build variables, so we can now use them in our build process later on. With these four environment variables encrypted (the keystore, the keystore password, the key alias and password), we’re good to go.
Build script
For our Android builds, we want to:
- Make the cloned directory writable (I was getting build failures without this, hence the
chmod
) - Write the keystore file into our repository so it can be used by the build process
- Switch to the Android directory and run the
assembleRelease
gradle task, which will produce a signed APK.
In the codemagic.yaml
file, it looks like this:
- npm install
- echo "sdk.dir=$HOME/programs/android-sdk-macosx" > "$FCI_BUILD_DIR/android/local.properties"
- |
chmod -R 777 $FCI_BUILD_DIR
echo $CM_KEYSTORE | base64 --decode > $FCI_BUILD_DIR/keystore.jks
# build Android
cd android
./gradlew assembleRelease
For Android, it’s really that easy! For iOS, it’s a little more involved …
iOS
In order for our builds to complete successfully, we need to load both our certificate and the .mobileprovision
file from App Store Connect onto the build agent. In this example I have encrypted the signing certificate as CM_CERTIFICATE, the password for the certificate as CM_CERTIFICATE_PASSWORD and the provisioning profile as CM_PROVISIONING_PROFILE.
Build script
This looks a bit more complicated than the Android equivalent, and also uses ✨ fancy Codemagic CLI functionality ✨to make it work. Here’s what we’re doing in this build:
- Running
keychain initialize
to prepare for the import of our signing certificate into the build agent - Setting
PROFILES_HOME
to where our mobile provisioning certificate is about to be, and then creating that directory - Setting
PROFILE_PATH
to a temporary location withinPROFILES_HOME
- Writing our PROVISIONING_PROFILE to
PROFILE_PATH
, with a temporary name - Writing our signing certificate (CM_CERTIFICATE) to
/tmp/certificate.p12
- Adding this certificate to the build box’s local certificate store, using CM_CERTIFICATE_PASSWORD to do so
- Running the
xcode-project use-profiles
command - And then just basically changing into the iOS directory, running
pod install
, and usingxcode-project build-ipa
to build our app, while specifying a workspace and scheme to use
- keychain initialize
- |
# set up provisioning profiles
PROFILES_HOME="$HOME/Library/MobileDevice/Provisioning Profiles"
mkdir -p "$PROFILES_HOME"
PROFILE_PATH="$(mktemp "$PROFILES_HOME"/$(uuidgen).mobileprovision)"
echo ${CM_PROVISIONING_PROFILE} | base64 --decode > $PROFILE_PATH
echo "Saved provisioning profile $PROFILE_PATH"
- |
# set up signing certificate
echo $CM_CERTIFICATE | base64 --decode > /tmp/certificate.p12
keychain add-certificates --certificate /tmp/certificate.p12 --certificate-password $CM_CERTIFICATE_PASSWORD
- xcode-project use-profiles
- |
# build iOS
cd ios
pod install
- xcode-project build-ipa --workspace "ios/$XCODE_WORKSPACE.xcworkspace" --scheme "$XCODE_SCHEME"
The finished codemagic.yaml
After all that, you should have a codemagic.yaml
file that looks a bit like this:
workflows:
react-native:
name: React Native App
environment:
vars:
XCODE_WORKSPACE: 'CodemagicTestProject2'
XCODE_SCHEME: 'CodemagicTestProject2'
CM_KEYSTORE: Encrypted(...)
FCI_KEYSTORE_PASSWORD: Encrypted(...)
FCI_KEY_PASSWORD: Encrypted(...)
FCI_KEY_ALIAS: Encrypted(...)
CM_CERTIFICATE: Encrypted(...)
CM_CERTIFICATE_PASSWORD: Encrypted(...)
CM_PROVISIONING_PROFILE: Encrypted(...)
node: latest
scripts:
- npm install
- echo "sdk.dir=$HOME/programs/android-sdk-macosx" > "$FCI_BUILD_DIR/android/local.properties"
- |
chmod -R 777 $FCI_BUILD_DIR
echo $CM_KEYSTORE | base64 --decode > $FCI_BUILD_DIR/keystore.jks
# build Android
cd android
./gradlew assembleRelease
- keychain initialize
- |
# set up provisioning profiles
PROFILES_HOME="$HOME/Library/MobileDevice/Provisioning Profiles"
mkdir -p "$PROFILES_HOME"
PROFILE_PATH="$(mktemp "$PROFILES_HOME"/$(uuidgen).mobileprovision)"
echo ${CM_PROVISIONING_PROFILE} | base64 --decode > $PROFILE_PATH
echo "Saved provisioning profile $PROFILE_PATH"
- |
# set up signing certificate
echo $CM_CERTIFICATE | base64 --decode > /tmp/certificate.p12
keychain add-certificates --certificate /tmp/certificate.p12 --certificate-password $CM_CERTIFICATE_PASSWORD
- xcode-project use-profiles
- |
# build iOS
cd ios
pod install
- xcode-project build-ipa --workspace "ios/$XCODE_WORKSPACE.xcworkspace" --scheme "$XCODE_SCHEME"
artifacts:
- android/app/build/outputs/**/**/*.apk
- build/ios/ipa
A small note - the Encrypted(...)
are placeholder values. You’ll need to encrypt your own values on Codemagic that suit your build process.
And that’s it! Your React Native app should build with Codemagic now. If you have any questions or comments, be sure to drop into Codemagic’s Slack channel and let us know. Happy developing!
More articles about React Native
- Flutter vs React Native: A developer’s perspective
- How to build React Native UI app with Material UI
- How to develop and distribute iOS apps without Mac with React Native & Codemagic
- Create React Native app using Expo CLI or React Native CLI
- How to run React Native Detox tests on Codemagic
Lewis Cianci is a software developer in Brisbane, Australia. His first computer had a tape drive. He’s been developing software for at least ten years, and has used quite a few mobile development frameworks (like Ionic and Xamarin Forms) in his time. After converting to Flutter, though, he’s never going back. You can reach him at his blog, read about other non-fluttery things at Medium, or maybe catch a glimpse of him at your nearest and most fanciest coffee shop with him and his dear wife.