Secrets Management in iOS Applications

Julian Nadeau
2 min readDec 27, 2016

Secrets in iOS applications can be a bit of an issue, especially when you intend to make a project public. The problem here is that there is no good way to store and use secrets in a compiled application, so you end up with plain-text secrets littered about the codebase.

There are numerous solutions that exist but these seem to always end up with the conclusion of “whatever, just add it to the repo” or suggest building a full user authentication system where they log into your system via the application. The former is obviously an issue for an open sourced repo (less so for a private repo) and the latter adds a lot of overhead when sometimes you just want to use the Mapbox API.

In this article, I intend to tackle the issue of storing plain-text secrets in the repo.

Solving the secrets issue

For this problem, I found a method using encrypted JSON and Plist parsing to work quite well. Below, I explain how I set this up and how it works.

  • Store secrets in an ejson file somewhere in the repository. This is an encrypted JSON file using Shopify’s ejson library.
  • Create a bin folder at the root of the repo and add this ruby script to the bin/secrets file to the repo. Remember to run chmod +x bin/secrets
  • Add a Build Phase to the iOS app called Decrypt Secrets, the content of the script should simply be bin/secrets
  • Add a Secrets.swift file with these contents:
  • Add a Plist file called Secrets.plist but leave it blank for now. Add it to Github in this state.
  • Add Secrets.plist to the .gitignore file so secrets we write to it aren’t pushed to Github at all.
  • You can accomplish the above with this command, changing out Secrets.plist to use the path of your actual plist file:
git add Secrets.plist
git commit -m 'Add blank Secrets.plist'
echo "Secrets.plist" >> .gitignore
git update-index --assume-unchanged Secrets.plist
  • Done!

How does it work?

The system works by decrypting the secrets.ejson file to a plist file that is compiled into the application. The Secrets class reads this plist file from the bundle and into a Swift dictionary. The entire application can now access the secrets using Secrets.secrets()['my_key'] as? String .

This means you no longer have to store unencrypted keys in the repo — a huge win for security.

On build, the secrets are decrypted and stored in plaintext in the app bundle— this doesn’t avoid that. But it does limit the exposure of secrets in the repo by not storing them in plaintext outside the app bundle.

The only caveat I have seen so far is that all users must now have ejson installed and place the private key that corresponds to your ejson file’s public key on their file system for the app to work, but this is not really a major issue if you have access to the developers of the app.