Run multiple versions of your App on the same device using Xcode Configurations

S0, you have an App that you want to distribute to your internal testers, yet want them to run both the internal and App Store versions concurrently. Well, this post is for you. If you are also interested in how to make builds for your live/staging/dev environments then this is also for you, but I expand more on that in the second part of this post.

Configuring All The Things

To get anything working we need to be familiar with Xcode Configurations. Most of you will be familiar with them yet you might not know it. Typically, Xcode starts us out with two; Debug and Release. You will find them in your project’s info page.

An App’s bundle identifier uniquely identifies an App on a device. In order for us to run a different version of our App on one device, we will need a new bundle identifier for our test app.

Lets go ahead and create a new configuration “Beta” that will identify builds sent to our beta testers. We can create this as a duplicate of the Release scheme (which we will rename to App Store since thats what it will represent now). This will result in Debug, Beta and App Store as available configurations.

Now we need to head to the Apps Info.plist which controls the bundle identifier used. You will see the Bundle Identifier key there with a bundle identifier that is probably hard coded to the reverse domain name of your App. Change this to read $(PRODUCT_BUNDLE_IDENTIFIER). This might already be the default value, if so, awesome 🐒

Now you can head to your App Target’s build settings, search for “Product Bundle Identifier” and you should see it listed under Packaging. When you hover over “Product Bundle Identifier” you should see an arrow to the left which you can tap to expand the values set per configuration.

Now is the perfect time to update your Debug and Beta builds to use a different bundle ID

Different bundle identifiers mean we can run all these builds on the same device

At this point you have multiple bundle identifiers which you will need to configure in the Developer Center as well so Xcode can generate provisioning profiles for you. Luckily this can all be done using Fastlane’s produce tool:

produce -a galasko.dan.XcodeSchemesFun.beta --skip_itc

Naturally, you will replace galasko.dan.XcodeSchemesFun.beta with your own bundle identifier:)

Changing the display name of the App per configuration

To differentiate our Beta App from the App Store version we would like it to have a different name. This could be something as trivial as “MyApp” vs “Beta-MyApp”. I prefix the name as we dont really want the Beta part to be truncated if the App Name is long.

So head back to your Info.plist and add a new field called Bundle display name and set its value to $(APP_DISPLAY_NAME)

Again we need to head back to the Build Settings section of our App’s target (lots of back and forth I know 🏓). Along the top of the build settings nav bar you should see a plus sign.

Tapping the plus exposes a drop down, choose “Add User-Defined Setting”. Now scroll down to the bottom of the build settings and you should see your setting there under User-Defined. Click on the newly added setting, which is probably titled “New Setting”. Pressing Enter will let you rename it. We want this name to match the one we just used in our Info.plist

At this point you might have noticed that in our Info.plist we used the syntax $(ENVIRONMENT_VARIABLE_NAME). Xcode automatically injects environment variables into project settings enclosed in $().

Tying it all together

At this point you can now have multiple versions of your App installed on a device at any given time. 🎉🎉🎉

To change which configuration you are running in you can simply navigate to your scheme settings:

In the edit scheme menu you can either change the Run configuration or the Archive configuration to be the one you want to use. Typically Archive is used for making an archive that you can distribute and Run is for when you run from your machine

Since you are reading this post you probably have a team of testers and some kind of automatable workflow so this is the best time for you to consider using Fastlane (which I am not affiliated with) to automate your distributions. That way you dont have to keep heading to Xcode, changing the scheme configuration, and performing an archive every time. At the very least you could make a build from the command line using the gym tool:

$ gym --scheme "XcodeSchemesFun" --configuration "Beta"

But head to the main repo for more.

In part 2 I talk about how to change you API environment/endpoint using this setup so you can make different builds for your environments like live/staging/dev etc. Read it here 👀