Managing iOS Configurations per Environment in Xcode 4

Update 4/2014

I recommend using .xcconfig files for managing your configurations. Take a look at this post.

At Carbon Five we usually have 3 – 4 environments our iOS applications will run against: development, acceptance, staging and production. Often, the property values that are unique across environments are URLs to APIs that we are integrating with. There have been several approaches for managing different configurations per environment. Some have included conditional compilation or build-time file substitution. While all are valid approaches, I opted to use Xcode’s build configurationsĀ to manage configurations per environment. Here is how I did it:

Step 1: Modify Your Default PList File

Your default plist file is usually named ${PRODUCT_NAME}-Info.plist and is located beneath the “Supporting Files” group of your Xcode project. Add a new row/item of type String to the plist file and name it Configuration. Set the value to be ${CONFIGURATION}, the Xcode provided environment variable for the current build configuration.

Step 2: Create a Custom Environments.plist File to Store Configuration Data

Right-click on the “Supporting Files” group and select “New File” (New File > Resource > Property List). Save File as “Environments”, this will create a file named “Environments.plist”. Now that you have your custom plist file ready to be modified, add all of your build configurations (Debug and Release by default) of type Dictionary to the plist. You can also begin to enter custom properties underneath each build configuration (entered as myAPIURL in my example).

Optionally, you can segregate your property files per build configuration. For example, create plist files for each of your build configurations (i.e. Debug.plist, Release.plist).

Add Custom Properties to Configuration by Environment (Build Configuration)

Step 3: Make your Custom Configuration Available to your Application

Create a singleton or implement a strategy to load your configuration data on a per build configuration basis. You can determine your scheme’s current build configuration by reading the “Configuration” property within your default plist file (as applied in step one).

For example, here is a singleton that will load your configuration:

Now you should be able to access properties within your application by sending getter messages to the Environment instance.

Environment *myEnvironment = [Environment sharedInstance];
NSString *apiURL = myEnvironment.myApiURL;

Step 4: Add Additional Build Configurations to your Application

Within Xcode, select your Project within the left-hand navigation. Your project and target settings should be visible and editable. Next, select your project settings so that your build configurations are exposed (by default, Xcode creates the Debug and Release build configurations for you). At this point you can create a new build configuration for a custom environment (i.e. acceptance, staging or demo).

Also, don’t forget to add your custom configuration data for your new build configuration.

Now Your Ready!

Your application now has access to configurations per environment by use of build configurations. We’ve made the current build configuration readable by adding it to the default plist file. We used the current build configuration as an identifier for key’ing entries in a custom properties file (Environments.plist). The custom properties file contains configuration properties per build configuration. In order to read the custom properties programmatically, we created an object (Environment.m) to read the file. Finally, we added custom build configurations to support new, application specific, environments.

<

p>Some side notes:

  • One target, configurable schemes: You can change the build configuration you would like to use for a target by editing (or creating) your scheme. This will quickly allow you to run your target against different application environments.
  • Properties like bundle identifier and bundle name can be set by user defined properties and further specified by your build configurations. This will make deploying you application to devices easier.
  • The Xcode command line build tool has a parameter named “configuration”. This will allow you to build your application per environment (i.e. xcodebuild -configuration Debug).

About Rob Pak

Rob codes for a living at Carbon Five, in beautiful Santa Monica, CA.
This entry was posted in Mobile and tagged , , , . Bookmark the permalink.

23 Responses to Managing iOS Configurations per Environment in Xcode 4

  1. Lakshmikantan says:

    Thank you for sharing the Article.

    I do have a quick question here. When the final ipa is created, all the property values of different environments is added to the final app file, thus exposing internal API service URL’s or Keys.

    Is it how it is? Am I missing something here?

  2. Rob Pak says:

    That is true. If you have sensitive data such as keys or psuedo-private URLs then another approach should be taken.

  3. Joe D'Andrea says:

    This is exactly what I was looking for – thank you!!!

  4. Peter Brown says:

    Why do you do `self.myAPIURL = …` instead of `sharedInstance.myAPIURL = …`

    • Rob Pak says:

      I access the myAPIURL property via self during initialization. I do this out of convention. The convention being that initializeSharedInstance is an instance method whereas sharedInstance is a class method. So I prefer to scope access to the myAPIURL property to the instance of the method it’s bound to. As long as the sharedInstance instance has been allocated, you can access any of the properties that you desire.

    • Rob Pak says:

      I access the myAPIURL property via self during the initialization of an instance of Environment. I do this as a convention. I prefer to scope access to the myAPIURL property to the same instance the method is bound to. And because initializeSharedInstance is an instance method, I modify myAPIURL using self. Because sharedInstance is a class method and since Environment is a singleton, you would essentially be modifying the same property. It’s just a convention thing.

      Another reason is for provisioning the singleton instance of Environment in a thread safe manner. When the singleton instance is initialized, I have exclusive access to self. So I modify myAPIURL using self while it is locked.

      As long as the sharedInstance property has been allocated an instance of Environment, you can access any of the properties you desire. Your choice.

  5. Guest says:

    Thanks, this was a very informative article!

  6. Besi says:

    Hy there, I created a gist, which I have extended a little bit. One addition I have made is the ability to define a master environment, which will be used as a fallback for the other environments.

    You can find it on SO: http://stackoverflow.com/a/10739917/784318

  7. Sydney Pang says:

    Thanks for the post, Rob.

    I feel like it’s not absolutely necessary to use a plist (or multiple plists) to hold the properties. What if I just hardcoded all the property values in an singleton object that has static methods to return properties based on the result of [[[NSBundle mainBundle] infoDictionary] objectForKey:@”Configuration”]

    ex.

    // Environment.h

    #define API_URL_DEV @”http://dev.company.com/api/”
    #define API_URL_STAGING @”http://staging.company.com/api/”
    #define API_URL_PROD @”http://www.company.com/api/”

    // Environment.m

    NSString* configuration = [[[NSBundle mainBundle] infoDictionary] objectForKey:@”Configuration”];

    if(configuration isEqualToString @”MyDevelConfiguration”) return API_URL_DEV
    if(configuration isEqualToString: @”MyStagingConfiguation”) return API_URL_STAGING;
    else return API_URL_PROD;

    • Rob Pak says:

      Thanks for your feedback Sydney.

      A goal of mine was to leverage iOS’s built-in facility for build configurations so that I wouldn’t have to add environment-based conditional logic in my code. So since the approach I chose didn’t require additional conditional statements, I felt it was a cleaner implementation.

      Also, I personally dislike hard-coding configuration data in my code.

  8. Daniel Valencia says:

    Very useful post, by the way.
    Question, couldn’t you solve it as well with xcconfig files ?

  9. Lucas says:

    Hi, just to let you know, I’ve created a cocoapod called CMEnvironment (https://github.com/lucasmedeirosleite/CMEnvironment) based on your idea!

  10. Johnny Fuchs says:

    Late to the comment game, but this is fantastic. Thanks for the post!

  11. John Wu says:

    how would you deal with integrating facebook where they require you set your urlscheme in your info plist?

    • Rob Pak says:

      I believe that Facebook has their own iOS SDK. Facebook handles API integration for you and therefore negates the need to roll your own environment configuration manager. If you are rolling your own, i’d check to see if Facebook provides different integration environments for their API.

    • Jonah Williams says:

      I think Rob’s approach here only works for adding custom attributes and can not cover cases like URL schemes or other info.plist values which need to change per-environment.

      My solution has been to have environment specific versions of the info.plist and change the path to then in the build configuration or symlink them into place in a build script. Unfortunately that leads to a bunch of duplication which each environment repeating many of the same settings. A better solution might be to assemble the info.plist from a common base and environment specific values as part of the build process.

  12. qix says:

    Cool article! How would I do the following with this setup? “Properties like bundle identifier and bundle name can be set by user defined properties and further specified by your build configurations. ” Right now I create a separate target for my different identifier+name combos, with each target using its own Info.plist, but to be able to specify this via a custom property would be super handy.

    • Paul Booth says:

      I’d like to set the bundle id and name via the build configurations too but I can’t see how to do that. Can anyone explain?

  13. Thiago says:

    Good morning,

    I wonder if you could help me, how do I change the key to release or debug?

    Thanks

  14. Tyo Aditya says:

    Very useful post. I use it for one of my project. Thanks for sharing.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>