Managing Configuration-Dependent Variables in iOS Projects

A step-by-step guide for Xcode setup

Piotr Gorzelany
Aug 30, 2019 · 4 min read
Source: Pixabay

Almost every app nowadays has to connect to some kind of back-end service and, usually, those services are deployed in multiple environments.

A classic example would be the dev/staging/production environments. The development environment is used for daily deploys and for devs to test their code.

The staging environment is used for stable releases for testers and clients to test. We all know what the production environment is for.

In this article, I will describe a step-by-step guide of what I think is the most straightforward way to handle multiple environments in an iOS app.

Here is a high-level overview of the setup:

  • Add a project-level configuration for each environment.
  • Modify the Info.plist file to contain a field that will resolve to the active configuration name at runtime.
  • Use the active configuration from the Info.plist file to read properties from a configuration file at runtime

1. Add Project-Level Configuration for Each Environment

First, you will need to add all the environments as project-level configurations. You can do this by clicking on the Project -> Info tab in Xcode (see the below screenshot).

Example project-level configuration

You can now select which configuration should be used at runtime by choosing the configuration in the Build Scheme -> Build Configuration setting.

In the below example the Debug configuration is selected when the build is run.

Build Scheme

2. Modify the Info.plist File

OK, we are doing great so far but we still cannot do anything useful.

For one, we don’t know in which configuration the app is currently running. To help with that, we will modify the Info.plist file to include the current active configuration name.

Info.plist

The $(Configuration) variable is a special environment variable that Xcode will resolve to the current configuration name when compiling the project.

That means that if the project is compiled in the Debug configuration, the value under the Configuration property will be Debug. The Info.plist file is a good place to store this information as we can easily retrieve it at runtime:

let activeConfigurationName = Bundle.main.object(forInfoDictionaryKey: “Configuration”) as? String

The above code will fetch the active configuration name from the Info.plist file so we can use it at runtime.


3. Read Properties From a Configuration File Using the Current Configuration Name

We can now access the current configuration name at runtime, and we can use it to read variables from a configuration file.

Lets first define a Configuration.plist file that will look like this:

Configuration.plist

Note that the configuration names have to be the same as the project configurations we defined in step 1.

We can now access the properties from this file at runtime using the active configuration name:

let activeConfigurationName = Bundle.main.object(forInfoDictionaryKey: “Configuration”) as! Stringlet configurationFilePath = Bundle.main.path(forResource: "Configuration", ofType: "plist")!let configurationDictionary = NSDictionary(contentsOfFile: configurationFilePath)!let activeConfigurationDictionary = configurationDictionary[activeConfigurationName] as! NSDictionarylet backendUrl = activeConfigurationDictionary["backendUrl"] as! String

And that's it, we can now retrieve the backendUrl for the current active configuration!


Summary

In this guide, we have defined different configuration/environments and made additional set up to be able to fetch them at runtime.

As you can see, the process is not super easy but also not very hard. It is the setup that is usually recommended on blogs and SO. It works…

Unfortunately, it’s also not very safe since we are mostly using string APIs for fetching the properties and we have to make a lot of force unwraps or guard against nils.

When fetching the properties this way, a lot of things can go wrong:

  • App will crash if we make a typo in the configuration filename.
  • App will crash if the configuration file has an incorrect format.
  • App will crash if we make a typo in the property name.
  • App will crash if we cast the property to the wrong type.
  • App will crash if we try to fetch a property that is not defined in the file.

All of this happens at runtime and the compiler cannot guard you against any errors. There has to be a better way right?

In the next article, I will introduce a tool that I wrote that will automate all of this setup and generate a Swift wrapper class that you can use to access the properties in a type-safe way using autocomplete. Stay tuned!

Better Programming

Advice for programmers.

Piotr Gorzelany

Written by

Software engineer specializing in iOS develolment

Better Programming

Advice for programmers.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade