When developing iOS apps, managing sensitive configuration data like API keys and server URLs across different environments—such as development, staging, and production—can be a tricky task. These data must be kept secure, yet easily accessible during the build process.

This blog will show you how to streamline the management of sensitive data using .xcconfig files and environment variables, both in your local development setup and continuous integration (CI) pipelines, ensuring a more secure and efficient workflow.

Overview

The solution involves creating .xcconfig files for each environment, with placeholder values for sensitive data. These placeholder values are then replaced with real values from environment variables at build time.

For example, a ConfigDev.xcconfig file could look like this:

API_KEY = API_KEY_PLACEHOLDER
SDK_LICENSE_KEY = SDK_LICENSE_KEY_PLACEHOLDER

During the build process, the API_KEY_PLACEHOLDER is replaced with the actual API key specific to the development environment.

Local Setup

Step 1

Create a shell script like setup_env.sh and place it in your project, for example, under the Scripts/EnvSetup directory. Here is the script gist: Link

Step 2

Run the setup_env.sh script to create the required .env and .local.xcconfig files.

sh setup_env.sh

This will generate .env.dev, .env.stage, and .env.prod files, as well as corresponding .local.xcconfig files in the Configs directory.

Step 3

Open each .env file and populate them with the appropriate values for each corresponding environment variable.

Step 4

Before building your project in Xcode, run the update_xcconfig_from_env.sh script to substitute the placeholders in the .xcconfig files with values from the corresponding .env file.
Here is the update_xcconfig_from_env.sh gist: Link

sh update_xcconfig_from_env.sh dev

Replace dev with stage or prod as needed.

Step 5

Keep in mind that the .env and .local.xcconfig files should not be committed to version control, so make sure to add them to your .gitignore file. The project's build settings are set to use the .local.xcconfig files, so ensure you're always working in debug mode.

CI Setup

In your CI system, instead of using .env files, set up environment variables with the same names and values.

Before building the project, run the same update_xcconfig_from_ci_variables.sh script to replace the placeholders in the .xcconfig files with the values from the environment variables.

Script gist: Link

Benefits

This solution has several benefits:

  • Sensitive data is not exposed in version control.
  • It’s easy to manage configuration settings for different environments.
  • It’s easy to update configuration settings without modifying the source code.

Bonus

For an easy and robust way to use.xcconfig variables, I created a property wrapper named @XCConfigValue. Check it out here

The usage should look like this:

enum Environment {
  @XCConfigValue(key: "API_BASE_URL")
  static var baseUrl: String
  @XCConfigValue(key: "SUBSCRIBE_LIMIT")
  static var subscribeLimit: Int
}

Conclusion

Managing sensitive data across multiple environments in iOS projects can be challenging, but using .xcconfig and .env files provides a secure and flexible solution. By keeping sensitive information out of version control and streamlining the configuration process for both local development and CI systems, you ensure your app stays secure while maintaining an efficient workflow.

At Povio, we're experts in building secure, high-performance iOS apps. If you're looking for help with your next project or need guidance on best practices for managing sensitive data, reach out to us—we’re here to help you take your app development to the next level.