iOS: Managing multiple environments with a single target
Many times when starting an iOS project, you get this request from the client: “I would like to install the released version of the app and the test version of the app at the same time”. On iOS, each installed app must have a unique bundle id. So a common approach is to duplicate your project’s target and create a new MyAppDebug target, then set the bundle id for that target to something like com.slalom.myappdebug.
Creating a new target has some downfalls though:
- You’ll need to ensure each file’s Target Membership includes each target
- Additions to the info.plist for third-party vendors, such as API keys, will need to be added to the plist file for each target
- A section for each target will need to be added to your CocoaPods Podfile
A better approach would be to use a single target, but handle multiple environments using User-Defined Settings (UDS) for your target. This approach eliminates the need to maintain two separate Info.plist files and you won’t have to add podfile sections, while giving you flexibility to change things like bundle id, bundle name, and third-party API keys based on the environment (Release, Test, Debug, etc.).
Install release and debug app side-by-side
In this example, I’ll show you how to use UDS to install the release version and a debug version of our example app simultaneously. To handle this, we’ll create the new UDS (configured for each environment) and we’ll update the Info.plist file to read from newly created UDS. We’ll need UDS for two items:
- Bundle Name — the name that is displayed beneath the icon on the home screen. We’ll use: “Single Target” and “ST Debug”
- Bundle Identifier — the unique id for the app. We’ll use: com.slalom.singletarget and com.slalom.singletargetdebug
To create a UDS for the Bundle Name, select your Target and then go to the Build Settings tab. On the top ribbon, select the + and choose “Add User-Defined Setting”. This will create a new entry for us. Give the setting a name of “ST_BundleName”. The setting name can be anything, but I find it best to prefix all UDS with something meaningful to the project. Once added, expand the entry to show all of the environments. Enter your values for each configuration you have setup.
Once the UDS is created, we’ll need to update the Info.plist to use the values we setup. We use UDS by setting the values in the Info.plist using the convention of ${UDS_NAME}. So head to Info.plist and change the value for “Bundle Name” to ${ST_BundleName}.
Next, add a UDS for the Bundle Identifier giving it the name of “ST_BundleId”. Then change the “Bundle identifier” value in the Info.plist to be ${ST_BundleId}.
An example of adding the UDS and changing the Info.plist for the Bundle Name can be viewed below.
Now we just need to create schemes based on environments instead of targets. Managing schemes is beyond the scope of this post, but let’s assume you created a Release scheme and a Debug scheme. You can now run each scheme and allow both instances of your app to be installed simultaneously. The same would go for your client, with the common scenario being they downloaded the Release version from the app store and installed a Test version from TestFlight or HockeyApp. See below for the final setup of getting multiple versions of your app installed side-by-side.
Setup third-party entries in Info.plist
Having a single target means having a single Info.plist file. This can be problematic when third-party vendors require entries to be added to the Info.plist file. For example, when integrating with Facebook for authentication, you need to add entries for “FacebookAppID” and “FacebookDisplayName”. It’s common practice to setup a Facebook app for Release and another for testing. So how can we setup different values for those entries based on our environment? User-Defined Settings of course!
So the next time you find yourself creating a new Target, determine if it’s really necessary, or if User-Defined Settings can give you what you need.