Tutorial

Using Xcode Configuration (.xcconfig) to Manage Different Build Settings


During the development lifecycle of a software project, you probably create different builds at various stages. At the early stage, there will be the one that conforms to your local configuration. When you are ready to move to the next stage, there is another build that will be utilized by your QA group to test the features and bug fixes. When the app passes all tests and got the QA team’s approval, you will create another version which is sent to your customers for beta test before pushing it to App Store. At last, there will be the production-ready application, once the client is satisfied with the build that you have sent. All of these builds are not exactly the same, each of which usually has some specialties and a slightly different configuration.

For example, if the app needs to connect to a backend, it is very likely the app is connected to the test environment during QA tests. The build is probably configured with a test URL. When you move to the next stage, the other build will have another URL for connecting to the staging/production server. On top of that, you may not be showing the same level of information when an error occurs in the application for all the builds.

How can you efficiently manage all these build variants with the same code base in Xcode? This is what I want to discuss in this tutorial and show you how to create multiple configurations with the help of Xcode.

How Can You Manage Multiple Builds?

There are different approaches that can achieve it. One is to create different targets, each one of which employs different Info.plist. Each time a target is selected, a different Info.plist will be used, hence we will be able to differentiate variables like token, URLs for different builds.

It could also be achieved by using Bundle Identifiers. Defining different preprocessor macros will control the conditional compilation of various chunks of code.

Eugene Trapeznikov has already covered this approach in this excellent tutorial. If you haven’t read that, please do his tutorial – How to Use Xcode Targets to Manage Development and Production Builds that covers all the key concepts.

The alternative approach is to put your build configuration settings into .xcconfig files and refer those in your project info. Then you can build a different version of an app by simply changing the scheme. This is what I am going to walk you through in this tutorial. Putting build configuration settings into files is a huge win for configuration management.

Creating the Build Configuration

First, let’s create a new project using Xcode. Or if you prefer, start with an existing one that is available on your computer. Select the top-level element in the project navigator and make sure the “YOUR_PROJECT_NAME” item in the Project section is selected. Once done, you should see that Xcode already provides you two different configuration levels: Debug and Release. If you didnt’ aware that before, this means you can create a build for debug and another one for release with a different setting.

Using Xcode Configuration (.xcconfig) to Manage Different Build Settings 1

Now we are going to create a new configuration. Let’s just call it “Staging”. Click on the + sign right beneath the configuration list and select “Duplicate Debug configuration” as it’s much easier to remove the things we don’t want from the Debug configuration than putting back in those we need to the Release configuration.

Using Xcode Configuration (.xcconfig) to Manage Different Build Settings 2

In the first cell of the row that just appeared, fill in Staging. Once done, you now have three levels of configuration.

Using Xcode Configuration (.xcconfig) to Manage Different Build Settings 3

Using Xcode Configuration File (.xcconfig)

As mentioned we use Xcode configuration file (.xcconfig) instead of using conditional compilation blocks to manage the build settings (such as which tokens, api keys, urls of backends should be used).

If you don’t know what an Xcode Configuration file (.xcconfig) is, it is actually a key/value based file. You can store your build settings in the form of key/value pairs, similar to what you did in dictionaries. By using a .xcconfig file, it is very easy to define build parameters for each build. You will understand what I mean in a while.

Now go back to the project to create a .xcconfig file. In the project navigator, right click the project folder and chooose New file…. In the dialog that pops up, select the Configurations Settings File. In the next screen, give it the name “Staging” and make sure the targets checkboxes are all unchecked, because you don’t want to include this in your app’s bundle.

Using Xcode Configuration (.xcconfig) to Manage Different Build Settings 4

Now go to your project’s info screen, under the configurations section expand the list and select the xcconfile “Staging” from the drop down box.

staging-configuration-file

Once done, you can repeat the process for the main target and choose the Debug file. Also, repeat the process for the Release Target.

staging-configuration-file-2

Changing the Build Information

Once you have the Xcode configuration files setup, it is pretty straightforwad to change the build. Say, you want to change the build information like app name, app version, bundle identifier, and bundle version for each build, you can edit each of the .xcconfig file like this:

Debug.xcconfig:

Staging.xcconfig:

Release.xcconfig:

You can use your configuration variables in project settings, info.plist and entitlement files. In this example, we will use it inside info.plist to change app name,app version, and bundle identifier like shown below:

Using Xcode Configuration (.xcconfig) to Manage Different Build Settings 5

For clarity, I am using a custom prefix “IS” for IntensifyStudio as the name of my little development studio to distguish the custom key from the default one.

Note: Changing the bundle identifier will require you to create more provisioning profiles.

Changing App Icon

With the Xcode configuration files, you can now easily configure the app icon for different builds. The procedures of customizing the App Icon for different builds are the same as the one as we discussed in the previous section, except that the variable will be used inside Build settings. Edit each of the .xcconfig file with an entry like this:

Debug.xcconfig:

Staging.xcconfig:

Release.xcconfig:

Once done, switch to Build Settings and replace AppIcon with the variable ${IS_APP_ICON}:

Using Xcode Configuration (.xcconfig) to Manage Different Build Settings 6

Then create a couple of the new iOS App Icon sets in Assets.xcassets and rename them accordingly.

Using Xcode Configuration (.xcconfig) to Manage Different Build Settings 7

Drag and drop your images in and you’re good to go!

Accessing Variables from Code

Xcode configuration files are really powerful and can be used for other settings as well. Say, if you have different API key and backend URL for different builds, you can specify them in each of the .xcconfig file. Then in your code, you can retrieve them back. Here is an example:

Debug.xcconfig:

Staging.xcconfig:

Release.xcconfig:

Next, add those variables to Info.plist by creating additional fields like this:

Using Xcode Configuration (.xcconfig) to Manage Different Build Settings 8

You can read the values from the plist file at runtime using the sample code below:

The code is pretty straightforward. We are accessing the Main Bundle to retrieve the information that we would like to use. Then, we remove all the backslashes from the string if the value returned is a URL.

Switching Between Build Configurations

To switch between build configurations, you can simply change the scheme by holding the option key and click on the scheme at the top. You can then select your preferred build configuration.

Using Xcode Configuration (.xcconfig) to Manage Different Build Settings 9

Conclusion

Xcode Configuration file is a powerful way for configuring different build configuration, allowing you to manage build variant easily. What do you think about the tutorial? Please let me know in the comment section if you have any question about xcconfig 😉

iOS
How to Beta Test Your App Using TestFlight
iOS
Working with Core Bluetooth in iOS 11
Tutorial
Working with Auto Layout Visual Format Language and Programmatically Creating Constraints
  • plam4u

    plam4uplam4u

    Author Reply

    I like a lot the configurations setup but I don’t like that Xcode recompiles the project when you change the config. Because of that, I tend to avoid configuration switching.

    How do you deal with that?


    • Ziad Tamim

      Ziad TamimZiad Tamim

      Author Reply

      In general, Xcode relies on file modification times, so it should not recompile files where that doesn’t change.


  • Ziad Tamim

    Ziad TamimZiad Tamim

    Author Reply

    Thanks Jhonny 😉 If the data is extremely sensitive then it should never be stored offline on the device because all devices are crackable. One of the options is to use Keychain for storing your data securely. However its encryption is based on the pin code of the device and users are not forced to set a pin, so in some situations, the data may not even be encrypted. In the addition to that, the user’s pin code may be easily hacked.


  • Tyrone Zhang

    Hi there, It’s really a great article, and I do not know how to use this approach in project witch has include cocoapods? Because cocoapods has it’s own configuration file, such as a file named “Pods-MyProject.staging.xcconfig”.


    • Ziad Tamim

      Ziad TamimZiad Tamim

      Author Reply

      Thanks Tyrone!! You can do that in many ways. One way is to “#include” CocoaPods’s build settings files inside your “*.xcconfig” files and reference those files in your project, info, configurations area.


  • Francisco G

    Switching between code configurations causes the pbxproj file to change which is tracked by version control. Is there a command to run different configurations that does not cause a change?


  • Kamil Chlebuś

    Is it possible to keep these custom variables (like BACKEND_URL) in a separate plist file e.g. Config.plist? I tried to do that but variable keys are not extracted to correct values. I’ve got “$(BACKEND_URL)” instead of a correct value “http://…” from xcconfig file. Any ideas? I’ve based on this solutions to load data from custom plists file – https://stackoverflow.com/questions/39910461/how-to-read-from-a-plist-with-swift-3-ios-app


    • Ziad Tamim

      Ziad TamimZiad Tamim

      Author Reply

      It’s a good practice to do it like shown in this tutorial which makes it more understandable for other developers in your team. I would not recommend that.


  • Nuno

    NunoNuno

    Author Reply

    Very nice, I usually have multiple info.plist files. Info-debug, Info-staging and Info-release and then assign each in the build settings to its respective scheme. Using the xcconfig files seems much cleaner. Will definitely update my projects in the morning with this. Thanks for the tutorial. Cheers


    • Ziad Tamim

      Ziad TamimZiad Tamim

      Author Reply

      Thanks Nuno!! I’m glad it helped 😉


      • Nuno

        NunoNuno

        Author Reply

        It did 🙂 too bad there’s no character escaping by Xcode. I also found a neat solution not to propagate string replacement to the code:

        SLASH=/
        FS_BASE_URL = http:$(SLASH)/services.domain.com/api/v1


        • Chathuranga Mohottala

          I use following format to store urls in the xcconfig file.
          BASE_URL=http:/$()/www.domain.com
          By using it like this, you don’t need to replace the slash characters


  • disobedient media

    Does anyone know which configuration is used when using TestFlight? I have the standard Debug / Release schemas in my project.
    I’d really like to use this, but we use testflight with internal and external testers and I’m unsure how it would work. I’m giving it a go, but would appreciate any info in advance.
    I can imagine that the release scheme would have to be used, but I’m hoping it’s the Debug one…
    Thanks


    • Nuno

      NunoNuno

      Author Reply

      When building for test flight, if the release scheme is selected, the configuration file used is the one that is assigned to the release configuration in the project’s info tab.


  • Asaduzzaman Shuvro

    How could i add manual provisioning profile for different build configuration. i think you should extend the tutorial with real device testing or provisional profile.


  • ismail bozkurt

    Hi @ziadtamim:disqus ,
    The way you described is really cool. However I encountered an issue, with the Bundle Ids. After applying your setup, I can see on the Project target section the “Bundle Identifier” and “Display Name” is being updated correctly. But when I tried to build on real device with the DEV.xcconfig and DEVBundleID, I get this error “Provisioning profile “DevProvProfile” has app ID “DevBundleID”, which does not match the bundle ID “ReleaseBundleID”.
    Once I check PRODUCT_BUNDLE_IDENTIFIER on the .xcodeproj file it holds the ReleaseBundleID not the the DEVBundleID .
    Can you tell how the Xcode on your case avoiding this problem. I might have missed something?

    Thank you kindly


  • Rostyslav

    RostyslavRostyslav

    Author Reply

    Excellent tutorial, thanks.


  • Surbhi

    SurbhiSurbhi

    Author Reply

    Hi Zaid,
    Excellent tutorial very well written I am new to swift and converting my existing Objective C project in swift. I have .xcconfig file in my project, how can I use the variable into it, when I tried your code like this

    func infoForKey(_ key: String) -> String? {

    return (Bundle.main.infoDictionary?[key] as? String)

    }

    class var appBrandID : String {

    let brandId = infoForKey(“BRAND_ID”)

    return brandId

    }

    This is giving error “Instance member ‘infoForKey’ cannot be used on type ‘BSConfiguration'”

    Please help me


  • Pedro

    PedroPedro

    Author Reply

    and if I want to have, in addition to the environments, several apps for different countries, how would you do it? Thank you!


Shares