Setting Up Multiple Build Configurations In Your Xcode Project

Guy Eldar
6 min readAug 25, 2016

--

One of the very first things every new iOS Engineer learns is that there are *exactly* two build configurations in your project — DEBUG and RELEASE. The first one is used to build and run the app on your device/Simulator during development. The second is being chosen when we want to archive the app towards submission on the AppStore or any other Ad-Hoc deployment that doesn’t require us to gather detailed debug information.

And indeed, when building a basic app, there won’t be any viable reason to add another custom build configuration to the party. Apple even took the time and real estate on their Build Configuration Documentation to mention that:

Apple’s note about the two basic build configuration provided with every project

Yet, when leaning towards building a full-scale, high-end app — in which you need to get to the highest robustness and bugs-proof level, you’ll find it very difficult to stay on top of things with only two build configurations. The two immediate triggers that will make things much harder to handle are these:

  1. Setting up a Beta testers community that will get an early access to the pre-AppStore builds and later on after launch, to the newest features that are still not in Production.
  2. Remote teams and relatively large in-house stakeholders who constantly need to stay in the loop of development and QA. Most chances are that due to that fact, you’re maintaining a separate server (i.e Staging) for these peeps to test and play around with the app versions.

Once you’ve found yourself in one of the situations described above, you will quickly feel the inevitable need of another build configuration in Xcode.

And that exactly what happened to my team at HoneyBook. Over here, we have our Engineering team located on one side of the world (Tel Aviv) and the Product/Marketing/Operations on the other side (San Francisco). In addition, since we have a pretty big users base already, it was a no-brainer for us to gather ±100 of our most loyal members and form a beta testing task force by allowing them to take part in the prototyping and development process of our new Mobile product.

Once we started rolling out builds we understood that we need to separate the beta testers from the internal stakeholders. The testers are real users that work in front of our Production DB and our in-house users are working in front of our dummied data Staging DB.

Step 1: Add New Build Configurations

As a first step, we’ve created two additional build configurations, Staging and BetaTesters, by heading into the project’s “Info” tab and duplicating the Release configuration as a baseline.

Duplicating a base configuration to create a new one (Original: Apple’s Project Editor Documentation)

So we ended up with this configurations list:

Our two new Build Configurations (Staging, BetaTesters), marked with red arrows

Step 2: Matching Between API Keys and Their Respective Build Configurations

We use several tools that define their deployment configurations on their own — Fabric, CodePush (Yes, we’re working with React Native), iOS Push Notifications, Appsee. For example, CodePush (that takes care of our JS on-the-fly updates) has its own 4 “deployments”, exactly like we just defined on Xcode.

As almost all 3rd party SDKs work, there’s a unique API key provided with the SDK which is being used by the remote server to identify the SDK client. Pretty Standard. On Xcode, the natural way to do that is by adding these keys to their respective slot in the info.plist file. The fun part begins when we’re required to adjust this API key differently for every build configuration we add. We’ll use the User-Defined Settings to solve this issue. These settings allow us to add another setting of our own to the already-too-long Build Settings tab of our target. We do that by clicking the ‘+’ button on the top bar of the Build Settings tab. This is how it looks after we added 3 User-Defined Settings.

Our new User-Defined Settings in our target’s Build Settings

Now, we could just take these new User Defined Settings, head to our Info.plist file and replace the hard coded API keys with these dynamic variables. The effective result is that every time we release a version, it will take only the relevant keys, according to the configuration we’re currently targeting.

Our Info.plist now contains the new, dynamic variables

Step 3: Making Our Static Libraries Obey

We’re building our app with React Native, which means we have several static libraries that we integrate into our project (i.e RNMixpanel, CodePush). But static libraries are not there only for RN of course, and if you’ve added some static libs to your project — that’s for you.

Once we were done with all the configurations mentioned above, we were optimistic enough to try and build the project with one of the new Build Configurations. Soon enough, we learned that static libraries do NOT behave well with new, custom configurations. That’s because of the simple fact they output their products to either Debug or Release $(BUILD_DIR). Just because that’s what they know. After a lot of frustration, we’ve bumped into this thread on Stack Overflow. It simply suggested to add this line

$(BUILD_DIR)/Release$(EFFECTIVE_PLATFORM_NAME)

as a new path to the Header Search Paths, Framework Search Paths and Library Search Paths Build Settings of our target. By that, we instruct the linker to look for the static libs’ headers and .a files in the respective Release folder (cause they’re being output to the Release folder on not to the Staging one — in our case).

Adding the new path to our Header Search Paths

Step 4: Push The Button!

And then it finally happened. At last, we were able to build our project with the new Build Configurations! We’re all set for the final step — archiving the project towards distribution (Ad-hoc/AppStore etc.).

The only thing that’s left to do is pointing the Archive scheme of our project to the desired configuration. Most of the engineers usually don’t hangout in those places, just because they don’t need more than the good old two-configurations preset.
You can access the ‘Edit Scheme’ dialog by pressing the Schemes area (marked with a red arrow in the image below). A dropdown list with all the project’s schemes will be displayed. Now press the ‘Edit Scheme’ option at the bottom of the list.

Choose ‘Archive’ in the left actions pane and then choose the desired build configuration from the list on the right.

The ‘Edit Scheme’ dialog that allows us to change build configuration for each action in our project

From now on, every time we archive our project, we should be aware of the fact that the project, alongside with all the dynamic settings, will be archived according to a specific set of rules.

--

--

Guy Eldar

SW Engineer, Entrepreneur and a Product Manager . Ex-Face.com (Acq. By Facebook), Ex-AppMyDay (Acq. By HoneyBook). Currently Sr. Product Director @ HoneyBook