Keeping secrets out of Git in your iOS app

Simple ways to protect your API keys

I have published an improved and remastered version of this article, covering my current approaches for protecting secrets. It builds upon what I discussed here and also contains a cleaned up version of everything in this article as well. The link above is to the article on Lord Codes, it can also be found here on Medium. Thanks 🙏.


Almost any iOS app will need to use private values, such as API keys, HMAC secrets or passwords. A simple approach to including these in your app would just be to put them in the code or in the Info.plist file. The problem with this approach is that these files are included into Git and so would be visible to anyone with access to the source code.

Protect the source code

At first thought the solution might seem simple, protect the source code so that people can’t get access to the secrets. Unfortunately, this isn’t always going to be possible as many apps are available open-source, where everyone needs access to the source code. On top of this iOS apps are available to users, who can decompile or analyse the contents of the app to find these secret values.

Exclude the files from Git

To allow the code to remain open, you can simply have the files containing the secrets ignored from Git. Anyone who needs to build the app with these values included can then simply create the required files. For an open-source project, the details of doing this can then be included in the README or on a wiki page.

For an iOS app, this would involve every developer either creating a source file or a plist file and adding it to the project. You would have to be careful to ensure references to these files remain in the project settings, even when the files aren’t included into Git.

xcconfig

My preferred solution is to store secret values within xcconfig files, one for each build type or environment. These files let you configure build config values for different environments, minimise setup for developers and keep the secrets out of Git.

  1. Create example versions of the files within the project.

I prefer to place them into a BuildConfig folder to keep them separated from other files. Developers will simply need to duplicate them, rename them and add the required values to them. The example files can specify the list of keys, but just don’t include the secret values. One file will be needed for each build configuration, such as debug and release.

debug.example.xccconfig

API_CLIENT_ID = API_CLIENT_ID_FOR_TEST
API_CLIENT_SECRET = API_CLIENT_SECRET_FOR_TEST

release.example.xccconfig

API_CLIENT_ID = API_CLIENT_ID_FOR_PRODUCTION
API_CLIENT_SECRET = API_CLIENT_SECRET_FOR_PRODUCTION

You will want these files added to the project so that they appear within Xcode. However, make sure you remove them from all targets in the Target Membership area of the File Inspector.

2. Create the real files for each build configuration.

Within these make sure to substitute the example values for real ones.

debug.xccconfig

API_CLIENT_ID = 123456789
API_CLIENT_SECRET = abcdefgh

release.xccconfig

API_CLIENT_ID = 987654321
API_CLIENT_SECRET = hgfedcba

As with the example files, you will want these files in the project, but they don’t need to be attached to any targets.

3. Add the real files to the .gitignore file.

4. Select these files in Project Configurations settings

You will need to select the config files within the project file under Info → Configurations. Make sure to set them for each build configuration and for the target you need.

Project Info pane

Value usage

Any values included in the xcconfig files are available as environment variables within project files such as Info.plist. If you want to use the values in your code, you will need to create a property within Info.plist that links to the environment variable, using the form $(API_CONFIG_KEY).

Edit (01/02/18)
Storing secrets in Info.plist isn’t the most secure way to do it. If your app has security concerns, then you may want to put them elsewhere. This article was focused on keeping them out of Git. I will add an edit when I can on ways of using these environment variables from the code with alternatives to Info.plist.

Edit (03/02/18)
Using Sourcery, you can generate a Swift struct that contains the secrets specified in your xcconfig files.

  1. Add Sourcery to the project.
  2. Create a stencil file, such as AppSecrets.stencil. List all the secrets you want to access from code in here, as Swift constants.
struct AppSecrets {
static let secretKey="{{ argument.secretKey }}"
}

2. Add a build phase to generate AppSecrets.swift.

Tools/Sourcery/bin/sourcery --sources Sources --templates Templates/AppSecrets.stencil --output Generated --args secretKey=\"$SECRET_KEY\"
  • The path to the Sourcery executable will depend how you install it. In my case a standalone version of the tool is contained within the project.
  • The sources argument is required, even though it isn’t needed in this situation, so just enter a valid directory in your project here.
  • The templates argument is a path from the root to AppSecrets.stencil.
  • The output directory is where AppSecrets.swift is written. The directory needs to exist. You could add ‘mkdir -p Generated’ at the start of the build phase to make sure it does.
  • Arguments are separated by commas in the form: arg1=one,arg2=two. I have escaped the contents of SECRET_KEY incase there are any special characters within it.

3. Add AppSecrets.swift to your .gitignore file, to keep it out of Git.

4. Build the app, then add AppSecrets.swift to the project, so that it is compiled and linked to the project target.

Note: If your secrets don’t require different values for different build configurations, you can skip the xcconfig part. Instead you can list your secrets as variables in a shell script and source it into the build phase.


By using the above approach, you will be able to keep your build secrets out of Git, whilst keeping it simple for new developers to the project.

Please feel open to reaching out to me on Twitter @lordcodes with any questions or thoughts you have, or about anything else.

If you like what you have read, please don’t hesitate to share the article and subscribe to my feed if you are interested.

Thanks for reading and happy coding! 🙏