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.

The image of Ben LokashBen Lokash
Sep 22, 2022
3 min read

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:

  1. Create the .app artifact by running xcodebuild in a pre-build script

  2. Distribute the artifact using the App Center CLI

  3. 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.

Set up for simulator

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.

Set up for simulator

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.

Set up for simulator

Pre-build Script

Branching paths in

    if [[ $IS_CI_SIMULATOR_BUILD ]]; then
      bash ./scripts/

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:${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

    appcenter distribute release \
      --file \
      --group "QA"
      --build-version $(($BUILD_VERSION + 1))



  • 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 want Release because it will include the bundled JS, whereas Debug will be looking for the metro server.

      • -destination: use "generic/platform=iOS Simulator" as the value to get a .app artifact

      • clean + 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 and sed, so we can increment it and use it for the current release

  • Zip the .app file

  • Distribute using appcenter distribute release

  • exit quits the builds early and shows a green checkmark


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.

written by
Logo of QuantumMob

We are a Toronto-based end-to-end digital innovation firm with a passion for building beautiful & functional products that deliver results.

The image of hire us
You might also like