Chapters

Hide chapters

iOS App Distribution & Best Practices

First Edition - Early Access 1 · iOS 14.2 · Swift 5.3 · Xcode 12.2

Before You Begin

Section 0: 3 chapters
Show chapters Hide chapters

Section I: iOS App Distribution & Best Practices

Section 1: 17 chapters
Show chapters Hide chapters

11. Advanced Build Configurations
Written by Keegan Rush

When you think about changing an app’s behavior, the first question that comes to mind is, “What code changes should I make?” As you’ve learned, changing code isn’t the only way to change what your app does.

By the careful composition of your project’s schemes, settings and configurations, you can change how Xcode builds your app.

In this chapter, you’ll take build customizations to the next level by learning how to:

  • Change build settings in a maintainable manner with build configuration files.
  • Sign your app differently depending on the build configuration.

It’s time to take your first steps out of Xcode’s comfy UI when it comes to preparing your build pipeline. Xcode will still do the compilation for now, but you’ll prepare your build settings with configuration files instead of Xcode’s build settings editor.

Build configuration files

In the previous chapter, you customized Emitron by adding your own build configuration and changing build settings depending on the current configuration.

The app’s bundle identifier and icon change based on build configurations. However, are there any other settings that change between dev, alpha and release build? To find out, you’d need to scroll through over a hundred different build settings. And, check to see if they change based on configuration.

Scrolling through build settings is tedious work. Wouldn’t it be easier if you could store all the customized build settings together in one place? That way, you’d know exactly where to go to modify build settings for a particular release type.

This is where configuration files come in. A configuration file is a simple text file where you can keep build settings and their values. The contents of a configuration file look something like this:

PRODUCT_BUNDLE_IDENTIFIER = com.raywenderlich.emitron.pias

SWIFT_VERSION = 5.0

On the left of the =, you write the name of the build setting. On the right, you set the value. And that’s it! Configuration files are easy to read and easy to change.

By using different configuration files for different build configurations, you can keep all the changes to settings in one place. If you tend to forget about the changes you made six months ago, your future self will thank you. :]

Benefits of configuration files

An Xcode project keeps all its build settings in the project file, known by the name project.pbxproj. This file holds everything unique about the project, including every target, configuration and scheme.

When you edit your project data, Xcode reads from and writes to the project file. While Xcode does a great job of letting you read and edit the project data, the actual project file isn’t very readable to human eyes.

Emitron’s project file is over 2,000 lines long. When opened in a text editor, you can see that it’s mostly confusing identifiers meant for Xcode that looks like this:

You can’t easily open project.pbxproj in your favorite text editor to change build settings. An .xcconfig file, on the other hand, is human-readable because it’s a regular text file.

Because project.pbxproj isn’t human-readable, it’s also subject to nasty merge conflicts. Whether changing one of many build settings, editing your targets, or adding new project files, two different developers changing the project file at once can lead to conflicts. Moving your customized build settings to a configuration file makes the project file’s job a little easier.

Another problem with Xcode’s build settings editor is its lack of undo/redo functionality. Command-Z does nothing for build settings. Instead, you need a good memory. Or preferably, you need a configuration file! :]

Refactoring build settings to configuration files

To learn how to set up build configuration files, you’ll refactor some build settings in Emitron and create a configuration file for each build configuration.

To start, open this chapter’s starter project.

Emitron has three build configurations: Debug, Alpha and Release. You’ll create a configuration file for each build configuration with specific build settings.

In Xcode’s menu bar, click File ▸ New ▸ File… and select the Configuration Settings File template.

Click Next. Set the file name to Dev. Change the group to Configuration located at Emitron ▸ Emitron ▸ Configuration. Leave the Targets selections unselected.

Click Create, and you’ll see your first blank build configuration file! Dev.xcconfig is the file you’ll use to store the build setting values that are specific to the Debug build configuration, but you’ll need to populate it first.

Adding build settings

Currently, both the bundle identifier and app icon change depend on the build configuration. They’re buried within the project.pbxproj file, and now you’ll move them to your new configuration file.

Add the following to Dev.xcconfig:

PRODUCT_BUNDLE_IDENTIFIER = $(inherited).dev

ASSETCATALOG_COMPILER_APPICON_NAME = $(inherited).dev

PRODUCT_NAME = rwenderlich Dev

Those screaming uppercase variables may look odd. However, they’re familiar conventions found in Xcode build settings.

PRODUCT_BUNDLE_IDENTIFIER is the actual name for the Product Bundle Identifier setting that you see in the Build Settings editor.

ASSETCATALOG_COMPILER_APPICON_NAME is the name for the Asset Catalog App Icon Set Name that you use to set the app icon.

The last build setting in the file, PRODUCT_NAME, is a new one: it refers to the Product Name build setting, which is the app name you see on your device.

For a complete list of build settings, look at the Build Settings Reference on the Xcode Help site at https://apple.co/3j2F9JL.

Now, look at the PRODUCT_BUNDLE_IDENTIFIER setting in the emitron target’s bulid settings, without taking values from Dev.xcconfig:

The default bundle identifier is com.raywenderlich.emitron.pias. The Debug configuration appends .dev to the default bundle identifier.

The configuration file does the same by using a special inherited variable. By using $(inherited), a build setting can dynamically refer to a default value, one further up the chain of resolution. That way, if the bundle identifier is declared at the project level changes, you don’t need to update configurations that use $(inherited) to reference the project level bundle identifier.

Build settings aren’t limited to referencing themselves with $(inherited). You can reference any other build setting as a variable in a similar fashion: $(SWIFT_VERSION) references the Swift Version build setting.

The ASSETCATALOG_COMPILER_APPICON_NAME setting follows the same strategy as PRODUCT_BUNDLE_IDENTIFIER to set the icon name. It uses $(inherited) to reference a default value, AppIcon in this case, and appends .dev to get the app icon specific to the dev build.

PRODUCT_NAME works differently. If you use $(inherited) Dev, the app name becomes raywenderlich (Dev). That’s a bit wordy for an iPhone’s home screen, and the name would appear as raywenderlich…. To keep it short and readable, you abbreviate the name to rwenderlich (Dev).

Build and run. Once the app runs, go to the Home Screen. Take a look:

The app name for the dev build is still raywenderlich. That’s because you’ve created a configuration file, but it isn’t applied to any build configurations. You’ll do that next.

Applying configuration files

In the Project navigator, click on the Emitron project to reach the project screen. Make sure you’re on the project’s Info tab.

In the Configurations section and next to the Debug configuration, click the icon to expand it.

Click on the dropdown to the right of the emitron target, not the Emitron project, and change its value to Dev.

Now, Xcode accounts for Dev.xcconfig when resolving build settings.

Resolving build settings

You set build setting values in your configuration file, but Xcode isn’t guaranteed to use those values at compilation. When Xcode needs to resolve the build setting, it chooses the most specific value it can find between the three different levels:

  1. Target Level
  2. Project Level
  3. Platform Default

Now that you’ve applied a configuration file at the target level, Xcode needs to look for values at the target level. When you add configuration files at either the project or target level, Xcode resolves build settings in the following order:

  1. Target level
  2. Target configuration cile
  3. Project level
  4. Project configuration file
  5. Platform default

Only a build setting declared at the target level in Xcode’s build settings editor can override a build setting declared in Dev.xcconfig.

Because of this, you need to make sure that no build settings at the target level are overriding your configuration file.

Still on the project screen, do the following to reach the Product Name build setting:

  1. Select the emitron target.
  2. Click the Build Settings tab.
  3. Change the scope to Levels.
  4. Search for Product Name.

Here, you can see that the setting has a value at the target level. This means that your value in Dev.xcconfig will never shine through; the Debug configuration’s resolved value is raywenderlich instead of rwenderlich (Dev).

To the right of the Product Name build setting title and above the three configurations, change the value to $(inherited).

Remember how using $(inherited) in a build setting lets that setting use a default value? In this case, setting the target level to $(inherited) uses the next value Xcode finds up the resolution chain. Now, the Debug configuration will inherit the value from Dev.xcconfig, which you set to rwenderlich (Dev).

Finally, build and run to see the results of your effort! Go to the Home Screen.

You’ll see Dev.xcconfig sets the Product Name.

Resolving bundle identifier and app icon settings

The Product Name gets its value from Dev.xcconfig, but the app icon and bundle identifier don’t do the same.

Back to the emitron target’s build settings editor, search Bundle Identifier.

Expand Product Bundle Identifier. Set the configurations’ values at the target level to $(inherited).

Next, search App Icon.

Set Asset Catalog App Icon Set Name at the target level to $(inherited).

Build and run.

Great job! Now, you’re correctly applying the build settings from Dev.xcconfig. You won’t notice any changes because Dev.xcconfig values are the same as previously set for the target level.

The dev build type is set up to use a configuration file, but the alpha build type is still configuring its settings in the Xcode UI. Next, you’ll use what you learned to make a configuration file for the Alpha build type.

Creating an Alpha configuration file

Now that you’ve set up the Product Name, Bundle Identifier and App Icon build settings to inherit values from configuration files, setting up a configuration file for the alpha build type becomes a lot quicker.

Follow the same steps you took to create Dev.xcconfig.

This time, create a Configuration Settings File named Alpha. Set the file’s group to Configuration. Leave the Targets selection unselected.

Next, replace the contents of Alpha.xcconfig with this:

PRODUCT_BUNDLE_IDENTIFIER = $(inherited).alpha

ASSETCATALOG_COMPILER_APPICON_NAME = $(inherited).alpha

PRODUCT_NAME = rwenderlich (⍺)

In the Project navigator, click on the Emitron project. Make sure you’re on the project’s Info tab.

Expand the Configurations section

Click on the dropdown to the right of the emitron target, and change its value to Alpha.

Under the Alpha configuration and to the right of the emitron target, click the dropdown menu. Then, select Alpha as the configuration file.

Since you’ve already configured the target-level build settings to inherit from the configuration files, that’s all you need to do!

You’re ready to test your changes. Change the active scheme to Emitron Alpha:

Build and run. Go to the Home Screen.

The alpha build gets its name from Alpha.xcconfig.

Configuration file for the release build

So far, you’ve created two configuration files: Dev.xcconfig for the dev build and Alpha.xcconfig for the alpha build. That leaves the release build without a configuration file:

Your configuration files for the dev and alpha builds exist to override default build settings. The release build is different. All of the default values for the build settings are set up for the release build:

  • Product Name: raywenderlich
  • Product Bundle Identifier: com.raywenderlich.emitron.pias
  • Asset Catalog App Icon Set Name: AppIcon

Since these default values don’t need to change for the release build, there’s no need to create another xcconfig file for release configurations here.

With your configuration files set up correctly to change bundle identifiers, product names and app icons based on the different build types, you’re almost ready to upload an alpha build to TestFlight to share with your time.

Before you hit Archive in Xcode and upload Emitron Alpha to App Store Connect, however, you’ll need to differentiate your code signing between alpha and release.

Code signing for different build types

Now that you have a shiny new alpha build, you also need a new provisioning profile for it. You need to do this because Emitron Alpha is a different app compared to the Emitron release build. In order to upload builds to App Store Connect, you can’t use the same provisioning profile for two different apps.

The Alpha build configuration is a duplicate of the Release configuration, with a few important changes: the most important of which is the bundle identifier.

By changing the bundle identifier, the alpha build type is essentially a completely different app from Apple’s point of view. That’s why you can install it on a device alongside the dev and release builds; they’re not the same app!

When you first created an app record for Emitron in App Store Connect, you also created a provisioning profile. With your provisioning profile in hand, you have what you need to take Emitron to the App Store. Now, you need to do something similar for Emitron Alpha. Don’t worry, though, you’ll be doing less clicking around in App Store Connect this time around!

First, look around Emitron’s code signing setup to understand how it’s done for dev, alpha and release builds.

In Xcode’s Project navigator, click the Emitron project to reach the project screen. Select the emitron target. Then, select the Signing & Capabilities tab.

The three sections correspond to your three different build configurations. Expand them and compare the differences.

In the Debug section, you’ll see that Automatically manage signing is enabled. This lets Xcode handle the work for you of creating your app ID, certificates and provisioning profiles. The bundle identifier is different from that of the release build, so having Xcode manage all of this is a nice convenience.

In the Release section, Xcode leaves you to manage your code signing.

Your release build is the most important of them all. Since you’re a code signing pro thanks to this book, you have all the knowledge you need to manage code signing yourself.

Putting in the extra effort for a release build can be worth it when you know what you’re doing. You’re in full control of code signing, so you can make sure that everything is set up exactly as you need it in App Store Connect and the Apple Developer Portal.

Signing the alpha build

Finally, take a look at the Alpha section.

Because it’s a clone of the Release configuration, code signing is an exact duplicate as well. Xcode configured the Alpha configuration with the Release configuration’s provisioning profile.

There are a couple of warnings at the bottom because the release build’s provisioning profile doesn’t match the alpha build’s bundle identifier. You’ll have to fix that before you can upload Emitron Alpha to TestFlight.

To fix the code signing for the alpha build, you need to do one of the following:

  1. Create your app ID and provisioning profile in the Apple Developer Center.
  2. Let Xcode automatically manage the signing of the alpha build.

Since it’s not as critical as the release build, it makes sense to let Xcode handle the dirty work for you. Click the checkbox next to Automatically manage signing.

You’ll see a pop-up asking to reset the Code Sign Identity and Provisioning Profile build settings.

Click Enable Automatic.

If your Team is set to None, you’ll get a warning:

Set your development team and let Xcode spin up your code signing setup for you.

And that’s it! With code signing in place, and all your build setting customizations coming from build configuration files, you’ve successfully set up an alpha build type from compilation to code signing. You’re ready to hit Archive and start distributing an alpha build to testers. Great job! :]

Key points

  • Build configuration files are a maintainable way to handle changes between builds.
  • A setting in a configuration file can reference itself, with $(inherited), or any other build setting.
  • The chain of resolution determines the resolved value of a build setting, so always check to make sure that the values in a configuration file will resolve as expected.
  • Code signing can change by your chosen build configuration.
Have a technical question? Want to report a bug? You can ask questions and report bugs to the book authors in our official book forum here.
© 2026 Kodeco Inc.