Mature iOS Development 1 - Xcode Build Process

This is the first article in a series of blog posts on tips to improve your iOS development process.

To improve the build process using Xcode, you need to understand the difference between 3 things:

  • Targets: Target is defined as a single product you are building. A common scenario of multiple targets in a same project would be building a free version of the app and a paid version of the app. You could put a compile flag on your code to determine at compile time which target you are currently building, and based on that check enable or disable certain content.
  • Configuration: Configuration is a set of definitions used at build time. By default, Xcode creates a new project with RELEASE and DEBUG configuration. I often create additional ones, like QA, STAGING, INTERNAL, RC, etc.
  • Schemes: Scheme is what brings everything together. With a scheme you can determine which targets to build (including test targets), which build configuration to use, and define different values for Run, Testing, Analyzing, and Archiving. Xcode by default creates only one scheme (unless you opted to create unit tests). That Scheme uses RELEASE configuration for Archive, and DEBUG configuration for everything else.

Create New Configurations

If you want to create new configurations for your project, select your project and go to Info. You will see the configurations that were created by default: DEBUG and RELEASE.

image

Clicking on the plus sign will let you duplicate an existing configuration. The reason why you can’t just “create” a new one instead of duplicating is that it will take all the default values set for the original configuration for the new one. Below, I will explain a little more about the values set per configuration.

image

As an example, I created a configuration named STAGING. I usually create that configuration to create a build that is a copy of my RELEASE build, with the different is that I will have my STAGING configuration to connect to a different environment of my web server, that will be a replica of my production environment.

image

Create New Scheme

Most of the time I create a new Schema for every Configuration, so below are the steps to create a Scheme for my new STAGING configuration.

Next to the buttons of where you select the target and run the simulator, you can click on the dropdown and select “Manage Schemes…”

image

Naming it after the configuration set is a good practice (I also rename the scheme created by Xcode to follow that convention).

image

Once created, we can click on ‘edit’ to view and modify the build scheme. There is a lot you can do here. You can set the order of targets and dependencies you want to execute, which arguments you might want to pass to the build, test targets, and more.

In the screenshot below, I’m showing how you select which configuration this scheme should use.

image

Now, if you close that window, you will see that you can select between the 2 schemes before you Build, Run, Test, Analyze, or Archive your application. Below I will show how to define specific values, like server url, api keys, signing keys for different scheme/configurations. After we set that, we can easily switch between those values by selecting those schemes, instead of having to make any code or .plist change.

image

Create Configuration Specific Values

Let’s set different values for different configurations. After all, that’s the reason for having different configurations.

Scroll all the way to the bottom of the Build Settings for your main target, or the target you want to edit, and you will see User-Defined settings. By adding settings, you can set values that are available both at compile time and run-time.

You can set the same value for all the different configurations. If you want different values per configuration, click on the dropdown, and you will be able to edit the value of each individual one.

image

You can define as many User-Defined values as you want (I’m not aware of any limits).

You can access those values on runtime with:

[[NSBundle mainBundle] infoDictionary][@"MY_USER_DEFINED_KEY"]

The most common settings I add to User-Define to applications I work on are:

  • BASE_URL: Determines which server URL I should connect.
  • API Keys: Keys for your API or third-party API’s. You can have different set of keys for development and production.
  • TARGET_IDENTIFIER or BUNDLE_IDENTIFIER: Determine the bundle identifier to use when building. I like to have different identifiers per environment so I can have different environments of my application installed on the same device.

One of the first things I do when I start a new project is to set different Bundle Identifiers per configuration, that way I can have all of them installed on the same device. That helps avoid confusion between opening a development build, or the production build, a release candidate. etc.

image

Now go to your Info.plist file. You can use .plist values to use your User-Defined values with the notation ${KEY}, or ${TARGET_IDENTIFIER} for what we just created.

image

If I build and run both schemes, it will generate 2 different builds, each one with a different build identifier based on the User-Defined value for the configuration.

You can see on the simulator screenshot below that we have 2 of the same app. That is possible because they both have a different bundle id.

image

Another tip is to set different assets file for your icons, and Bundle name, so you can differentiate the builds in your home screen.

Thank you for reading. I hope you find this information valuable. If you have any questions or comments, feel free to post below.

See more of my posts related to getting a more mature process for your iOS developement here