fabián cañas

Keeping Private API Keys Private

I used to keep sensitive iOS project information in a .gitignored plist like Mike Walker. Now I keep secrets in a few different .gitignored xcconfig files. It turns out that xcconfig files let me flexibly configure build targets for different environments. I now use this approach in closed-source projects as well.

xcconfig

An open source iOS app I have in progress shows how I do it now: https://github.com/fcanas/HabitForming.

There is a Keys.example.xcconfig in the repository. The README has instructions to copy its contents into Keys.xcconfig and replace the IDs and keys with appropriate values. The real key files are in the repository's .gitignore file.

PARSE_APPLICATION_ID = YOUR_ID_HERE
PARSE_CLIENT_KEY = YOUR_KEY_HERE
FACEBOOK_APPLICATION_ID = YOUR_ID_HERE

The Xcode project doesn't need a direct reference to the Keys.xcconfig files. The project instead has references to environment config files like this one:

#include "Keys.xcconfig"
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) PARSE_APPLICATION_ID=@\"$(PARSE_APPLICATION_ID)\" PARSE_CLIENT_KEY=@\"$(PARSE_CLIENT_KEY)\" FACEBOOK_APPLICATION_ID=@\"$(FACEBOOK_APPLICATION_ID)\"

The #include directive will find xcconfig files in the directory without the Xcode project needing a reference to it, meaning your project isn't ever in an inconsistent state on initial check-out. The second line makes all the values available as preprocessor symbols.

With this approach, you can keep a different Keys file for different environments (e.g. Dev-Keys.xcconfig, Stage-Keys.xcconfig, Prod-Keys.xcconfig) and tie them to different build configurations: Dev-Config.xcconfig, Config.xcconfig. You can then put any information that isn't sensitive in these configuration files.

You'll need to configure your project's build settings to use the configurations files like so:

Configuring a Target

There's a great additional advantage to having values as preprocessor directives. They're available as symbols in you app's plist, meaning you can create URL scheme handlers like fb${FACEBOOK_APPLICATION_ID} and they'll always be configured correctly for your environment (incredibly useful for Facebook sign-in in development and production environments).

You can also keep separate bundle identifiers for the same target (useful for things like Enterprise distribution, and separate installs on the same phone from the same target). You can also configure product names, or anything else.

All without maintaining multiple targets.

Recap

Use xcconfig files to manage service keys, app IDs, service endpoints, or anything else for different environments.

And reap the rewards.

  1. Manage different values for different environments
  2. Keep .gitignored files for keys out of Xcode
  3. Extensive opportunity for configuring targets with configuration files