
7 Techniques to Store API Keys in iOS Apps

API keys are a common requirement for integrating third-party services into your iOS app. But storing them securely is not as simple as dropping them into your code. If a malicious actor extracts your key, they could impersonate your app, abuse your API quota, or access sensitive data.
In this article, we’ll explore the most common techniques for injecting API keys into iOS projects — from simple compile-time constants to dynamic server-side provisioning — and discuss their pros, cons, and when (if ever) they’re safe for sensitive credentials. Besides considering the security of each method, we'll also look at the ease of use and the impact on the developer experience.
1. Hard-coding in Source Code
The simplest method is to define the API key directly in Swift:
let apiKey = "your_api_key_here"
- Advantages: Quick, zero configuration.
- Disadvantages:
- Key is stored in plain text in your repository and binary. Anyone can reverse engineer and extract it.
- If you want to support multiple environments, you have to setup a system like this yourself.
- Your API-KEY is part of your VCS and can be easily found by anyone.
- Not good to manage if you work in a team.
- Not easy to manage if you work with a CI/CD pipeline.
- Verdict: Fine for prototypes, never for production. But does not scale.
2. Xcode Build Settings + .xcconfig
Store keys in .xcconfig
files and inject them as build settings:
API_KEY = your_api_key_here
let apiKey = API_KEY
- Advantages:
- Supports multiple environments
- Avoids committing to main source files.
- Can be used in CI/CD pipelines.
- Can be used in different build flavors.
- Disadvantages: Still ends up in the binary, easy to extract.
- Verdict: Good for non-sensitive values, not for true secrets.
3. Environment Variables + Build Scripts
Pass secrets via environment variables during build, generating a Secrets.swift
file:
$echo "let apiKey = "API_KEY"" > "$SRCROOT/Secrets.swift"
print(apiKey)
- Advantages:
- Keeps secrets out of VCS
- Integrates with CI/CD
- Can be used in different build flavors.
- Disadvantages: Still embedded in binary, requires careful build environment setup.
- Verdict: A cleaner workflow, but not secure for high-value keys.
4. Local Property List or JSON in Bundle
Store keys in a Secrets.plist
or JSON file in your app bundle and read at runtime:
if let path = Bundle.main.path(forResource: "Secrets", ofType: "plist"),
let dict = NSDictionary(contentsOfFile: path),
let apiKey = dict["API_KEY"] as? String {
print(apiKey)
}
- Advantages:
- Centralized configuration
- Easy to swap per build
- Can be used in different build flavors
- Disadvantages:
- Fully readable to anyone who inspects the app bundle.
- Not easy to manage if you work with a CI/CD pipeline.
- Is a bit of a custom solution that needs to be explained to the team.
- Verdict: Acceptable for public keys, unsafe for private ones.
5. Remote Fetch from Backend (Recommended)
The safest approach is not to ship the key at all. Your app requests it from your server at runtime, after authenticating:
// Example fetch
let url = URL(string: "https://api.example.com/credentials")!
let task = URLSession.shared.dataTask(with: url) { data, _, _ in
// Store securely in Keychain
}
task.resume()
task.resume()
- Advantages:
- Keys never stored in binary
- Can be rotated easily
- Supports per-user or scoped keys
- Disadvantages:
- Requires backend infrastructure
- Offline use may be harder
- More setup code - more complexity
- Verdict: The only truly safe approach for sensitive secrets that gives you the most control.
6. Keychain Storage
Combine remote fetch with secure storage in the iOS Keychain:
KeychainHelper.save(apiKey, for: "API_KEY")
// Later
let apiKey = KeychainHelper.load(for: "API_KEY")
- Advantages:
- Persists securely across launches
- Hardware-backed on modern devices
- Is the most secure way to store a secret. Your API-Key can not be extracted from the binary.
- Disadvantages:
- Still requires initial secure delivery.
- Requuires network access to fetch the key - not applicable for offline scenarios.
- Not easy to manage if you work with a CI/CD pipeline.
- Verdict: The most secure way to store a secret. It's also the "Apple way" to store a secret. Your API-Key can not be extracted from the binary.
7. Obfuscation & Split Secrets (Last Resort)
If you must include a key in the app, obfuscate it and combine with a runtime-fetched component:
let part1 = "abc123".reversed()
let part2 = fetchKeyPartFromServer()
let apiKey = "\(part1)\(part2)"
- Advantages:
- Slows down casual attackers.
- Disadvantages:
- Still reversible, not a substitute for proper backend protection.
- Not easy to manage if you work with a CI/CD pipeline.
- Is the most complex solution compared to the other methods.
- Not easy to understand - might introduce bugs
- Verdict: The most secure way to store a secret. Your API-Key can not be extracted from the binary.
Conclusion: There Is No Secure In-App Secret
If the client has it, an attacker can get it. The only robust way to protect sensitive API keys is to keep them on a server you control and deliver short-lived, scoped credentials to your app after authentication. All other methods are about risk reduction, not elimination.
For production apps handling sensitive data, choose Remote Fetch + Keychain. For low-risk keys, xcconfig or build script injection can provide a cleaner workflow without hard-coding.