Creating React Native Simulator Builds in App Center
Using App Center for mobile CI/CD is simple, but it does not allow for much customization. Our team requested a simulator build to facilitate automated testing. After some research and tinkering, I was able to come up with a clever (read: hacky) solution.
Table of contents:
I generally like using App Center for mobile CI/CD. It is dead simple to get a pipeline up and running. However, it does not allow for much customization. I recently ran up against this limitation when trying to set up a build pipeline for iOS simulator builds.
Our QA team requested a .app
build to facilitate automated testing. I quickly realized that App Center did not support this - the default build config results in a .ipa
file for installing on physical devices. After some research and tinkering, I was able to come up with a clever (read: hacky) solution.
The general idea is:
Create the
.app
artifact by runningxcodebuild
in a pre-build scriptDistribute the artifact using the App Center CLI
Exit the build before the usual build steps run
Adding the build pipelines
We are going to set up two pipelines, one for building the app, and one for distribution. The reason we need a separate pipeline for distributions is that the standard iOS OS type in App Center will only let you distribute .ipa
files when what we need is .app
. We can get around this by creating a second pipeline using the “Custom” option for our OS. The custom OS type will let you distribute .zip
files, which will allow us to distribute our .app
files - all we have to do is compress them before uploading.
The first pipeline should be configured the same as any other iOS build pipeline you may have already set up. Set an environment variable (eg. $IS_CI_SIMULATOR_BUILD
) that we can use later in the build script to run the simulator build instead of the regular one.
The second will be a “Custom” app type, which has no build step but lets us distribute .zip
files, which will contain our .app
artifact.
We will also need to create an authentication token. We will use this later to authenticate with the App Center CLI in order to upload our build.
Pre-build Script
Branching paths in appcenter-pre-build.sh
if [[ $IS_CI_SIMULATOR_BUILD ]]; then
bash ./scripts/ios-sim-build.sh
fi
Our codebase should be build-agnostic, so we can use an environment variable to tell App Center to run our simulator build script at the end of the regular build script.
We also need to add an additional environment variable to our build pipeline called $APPCENTER_OWNER_APP_NAME
. You can find this value by going to the releases page of your release pipeline. The url should be in the following format: https://appcenter.ms/orgs/${YOUR_ORG}/apps/${YOUR_RELEASE_PIPELINE_NAME}/distribute/releases
Combine the two like so $(YOUR_ORG}/${YOUR_RELEASE_PIPELINE_NAME}
to get a string that will uniquely identify the release pipeline.
The simulator build script
#!/usr/bin/env bash
unset PREFIX
cd ios && pod install && cd ..
xcodebuild -workspace "ios/MyApp.xcworkspace" \
-derivedDataPath "./ios-sim-build" \
-scheme "MyApp" \
-configuration "Release" \
-destination "generic/platform=iOS Simulator" \
clean build
appcenter login --token $APPCENTER_USER_TOKEN
export BUILD_VERSION=`appcenter distribute releases list --app $APPCENTER_OWNER_APP_NAME | sed -n '1p' | sed "s/[^0-9]//g"`
# path may be different based on the name of your configuration
cd ios-sim-build/Build/Products/qa-iphonesimulator
zip -rq MyApp.zip MyApp.app
appcenter distribute release \
--app $APPCENTER_OWNER_APP_NAME \
--file PayPower.zip \
--group "QA"
--build-version $(($BUILD_VERSION + 1))
exit
xcodebuild
Ok, here’s the meat of the script:
xcodebuild
runs the build, resulting in the.app
file we need.-workspace
: path to the .xcworkspace file-derivedDataPath
: path to the directory where the temp build files will be stored-scheme
: usually the name of your project unless you have created custom schemes-configuration
: usually"Debug"
or"Release"
unless you have created custom configurations. You probably wantRelease
because it will include the bundled JS, whereasDebug
will be looking for the metro server.-destination
: use"generic/platform=iOS Simulator"
as the value to get a.app
artifactclean
+build
: clean cache files and start the build
AppCenter Distribution
We’ve got our
.app
artifact. All we have to do now is find a way to distribute it!Authenticate using
appcenter login --token $APPCENTER_USER_TOKEN
Get the previous build number using
appcenter distribute releases list
andsed
, so we can increment it and use it for the current releaseZip the .app file
Distribute using
appcenter distribute release
exit
quits the builds early and shows a green checkmark
Conclusion
After using this approach with my team for a few weeks, we identified some opportunities for improvement. We realized that the .zips
don’t come with build numbers or versions attached. We plan on adding this information to the file name before rolling out this solution more widely across the project.
Creating this pipeline has allowed our QA team to stay perfectly in sync with the development team. Before this approach, we had to manually run a build every time there was a push to develop
. This was time-consuming and error-prone. Now, builds are created automatically on every push, and QA is instantly notified. We think it is well worth spending the time to develop this for any React Native project that relies on automated testing.
Newsletter Sign-up
Receive summaries directly in your inbox.