Change your API endpoint/environment using Xcode Configurations in Swift

Often, as an iOS developer, you might find yourself working on an App that communicates with an API. This API might be an internal API built by the company you are working with so there are probably a few environments in the wild. This means that you will most likely be tasked with making builds that point at each one of these environments/endpoints.

What we don’t want to do is put these endpoints in our code so that every time we need to make a build pointing to a particular environment, we need to make a code change. If you want to be really helpful, you will allow those in your organisation to run these different builds concurrently. This means that testers can have their staging/dev/live builds installed on the same device at the same time. I talked about how to set this up here. The gist of that post was that you need a different bundle identifier per App that you want to distribute. This post will be using the principles of that article so definitely give it a read.

Making our endpoint an Xcode environment variable ⛳️

Before we can do anything, we need to prepare our Xcode configurations for the environments we want to use. As I mentioned in my previous post, we do this by adding Xcode configurations. Feel free to tweak this but a basic setup would look as follows:

Multiple configurations to cover all the environments we expect to make a build for

I typically have a Production and App Store configuration because I like to differentiate between in-house builds that point at production, versus the final build submitted to the store. This way I can turn off stuff like analytics for non App Store builds.

Now you want to head to your target’s build settings. We are going to create a new user-defined setting and for all intents and purposes, mine will be called MY_API_BASE_URL_ENDPOINT

This gives us an environment variable that will change based on which configuration we are running with. Unfortunately, we can’t yet use this in code as we can’t use the values of environment variables in Swift. What we can do is map this to our Info.plist file.

Now, when we want to access this in code it’s really simple:

let env = NSBundle.mainBundle().infoDictionary![“MY_API_BASE_URL_ENDPOINT”] as! String

Taking it a step further 🏋

I like to be really flexible in my debug configuration so that I can tweak my endpoint at any given time. To get this behaviour we can add swift compiler flags so that we can perform macro checks in our code. Typically I like to have a flag denoting debug mode, and App Store mode:

Now we have the power to change our environment when in debug mode. So, imagine we have a configuration class that defines our environment as follows:

struct Configuration {
    #if DEBUG
    enum Environment: String {
        case Production = "www.galasko.com"
        case Dev = "www.galasko-dev.com"
        case Staging = "www.galasko-staging.com"
    }
    let environment = Environment.Staging.rawValue//we can change this at any time
    #else
    let environment = NSBundle.mainBundle().infoDictionary!  ["MY_API_BASE_URL_ENDPOINT"] as! String
#endif
}

Now, only in debug mode have we defined our endpoint that is customisable via an enum. When run in any other configuration it will default to the values used in the Info.plist file.

To make a build, all we need to remember are the values of our configuration and we can do everything from the command line without changing a single line of code. Neat hey 😎.

Ultimate Setup 🚀

If you read my previous post then at this point you probably want to make different builds that point to the corresponding environments. So you would have different bundle identifiers for each configuration:

And a unique app display name for each one so your testers are never confused:

And there you have it folks. This coupled with Fastlane will save you hours of pain and has been a great experience for me during my adventures.

I hope this helps you! Good luck:)

✌️