CodeX
Published in

CodeX

iOS/Swift Series: [#08] Setup iOS environments

It’s best practice to have separate environments for your iOS apps, especially if they are communicating with any servers. For instance, consider an iOS app and three different available web backend environments: development, staging, and production.

What’s the best way to switch in the iOS app to use these different servers? What about all the other potential key/value pairs of information, like API keys — and what if some of that information needs to be secure and not checked into version control?

The simplest way is to create a file to store these environment variables as a string or other constants in the native language, like Swift. However, this may lead to numerous issues - whether it be for security reasons or overall bad practices involving hardcoding flag values or relying on macros. Instead, let’s use separate files that we can avoid checking into version control that explicitly outlines the properties of the app that will change on an environmental basis.

Project Setup

Open Xcode and create a new project based on the Single View Application template.

Name your project and set Language to Swift.

Adding Configurations

Every Xcode project includes two default configurations, Debug and Release. For some projects, these configurations are sufficient. However, assume for a moment that you are building an application that interacts with a web service. The web service defines two environments, Staging and Production. Configurations can help you to quickly switch between these environments with very little effort.

With the project opened in Xcode, select the project in the Project Navigator on the left. Configurations are defined at the project level. To inspect the list of configurations, open the Info tab at the top and look for the Configurations section.

During development, it may be necessary to switch between a staging environment and a production environment. Let us start by creating a configuration for each environment. Double-click Debug and change the name of the configuration to Debug (Staging). Click the + button at the bottom of the table and create a new configuration by choosing Duplicate “Debug” Configuration from the menu that appears. Name the new configuration Debug (Production).

If you also need the ability to switch environments for release builds, then repeat the above steps for the Release configuration. This can be useful if a client asks for a release build for every environment. If you are using an automated build process, then this is the way to go. Example:

  • Debug (Development)
  • Debug (Staging)
  • Debug (Production)
  • Release (Development)
  • Release (Staging)
  • Release (Production)

Storing the Configuration in Info.plist

To ensure that we have access to the configuration at runtime, we store it in the target’s Info.plist. This is very easy to do. In the Project Navigator on the left, select Info.plist, right-click, and choose Add Row to create a new key-value pair. Set the key to Configuration and the value to $(CONFIGURATION).

The configuration that is used during the build process is accessible through an environment variable, CONFIGURATION. This makes it easy for us to dynamically update the target's Info.plist during the build process.

Defining Schemes

With the current configuration stored in the target’s Info.plist, it is time to add the ability to easily switch environments. We do this by adding a scheme for every configuration. At the top left, click the active scheme and choose Manage Schemes.

Select the scheme and click the gear icon at the bottom of the table. From the menu, select Duplicate. Repeat this step one more time. You should have three schemes in total. Double-click the new schemes and name them Configurations Debug (Staging) and Configurations Debug (Production).

If you work in a team or use an automated build process, then it is interesting to check the Shared checkbox on the right to share the schemes with your teammates.

Select Configurations Debug (Staging) and click Edit… at the bottom to edit the scheme. We need to tell the scheme which configuration to use when the application is run. Select Run on the left and make sure the Info tab is open at the top. Set Build Configuration to Debug (Staging). Do the same for the Debug (Production) scheme, setting Build Configuration to Debug (Production).

Loading Configuration Details

There are several ways to expose or load the details associated with a particular configuration or environment. You can store them in a JSON file and include this file in the application bundle or you can store them in a property list for easy access. The downside is that other people can easily access the contents of your application bundle by downloading your application from the App Store. If the data is not critical, then that may be fine. If the environment details include sensitive data, such as tokens and API keys, then you need to look for a safer solution.

enum Environment: String {
case Staging = "staging"
case Production = "production"
var baseURL: String {
switch self {
case .Staging: return "https://staging-api.myservice.com"
case .Production: return "https://api.myservice.com"
}
}
var token: String {
switch self {
case .Staging: return "lktopir156dsq16sbi8"
case .Production: return "5zdsegr16ipsbi1lktp"
}
}
}

We also define a structure, Configuration. The Configuration structure declares a lazy stored property, environment of type Environment. The implementation is straightforward. We load the configuration from the target's Info.plist. Remember that we dynamically update and store the configuration in the target's Info.plist. We then use a simple technique to parse the configuration name and initialize the corresponding Environment instance.

import UIKitstruct Configuration {
lazy var environment: Environment = {
if let configuration = NSBundle.mainBundle().objectForInfoDictionaryKey("Configuration") as? String {
if configuration.rangeOfString("Staging") != nil {
return Environment.Staging
}
}
return Environment.Production
}()
}

Don’t forget to add an import statement for the UIKit (or Foundation) framework at the top to gain access to the NSBundle class.

Testing the Implementation

Open ViewController.swift and update viewDidLoad() as shown below.

override func viewDidLoad() {
super.viewDidLoad()
// Initialize Configuration
var configuration = Configuration()
print(configuration.environment.baseURL)
print(configuration.environment.token)
}

Select the Debug (Staging) scheme at the top and run the application in the simulator or on a physical device. This is what the output should look like in Xcode.

If you select the Debug (Production) or Release schemes, the production environment is automatically selected. It is a good practice to use the production environment as the default environment. This avoids possible configuration issues for production builds.

Creating Archives

In this tutorial, we only set the build configuration for running the application in the simulator or on a physical device. If you want to select a particular build configuration for archiving, you need to specify that in the scheme.

Click the active scheme at the top and choose Edit Scheme…. Select the Archive tab on the left and set Build Configuration to the desired configuration. The default is Release.

REFERENCES

--

--

--

Everything connected with Tech & Code. Follow to join our 900K+ monthly readers

Recommended from Medium

#Day14 — Exceptions in Python

SometMy solution for the 538 Riddle of the week.

MetaMetrics Interview #1 — Bobby Dresser

The latest price of saffron in the market inside and outside Iran

The latest price of saffron in the market inside and outside Iran , The latest price of red gold in the market inside and outside Iran, saffron cultivation, economic prosperity in saffron, medicinal plants, the price of saffron in the market inside and outside Iran, the latest price of saffron in the market, Iranian saffron

Amazon Aurora vs Amazon RDS and Amazon Redshift

What Is the Runtime Complexity or Big ‘O’ Notation?

Anomaly Detection Patterns using Azure Stream Analytics

What’s New in JUnit 5.3

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Hau NGUYEN (Leo)

Hau NGUYEN (Leo)

🎓 A true Software Engineer aspires to build a strong community and help other people grow up.

More from Medium

Integrate SwiftUI View in UIKit

Send data with navigation from View controller to SwiftUI view

How it works Combine pipeline?

How to preview UIViewControllers using SwiftUI