SHIPSAFE guides / Guide

Developer guide

Hard-coded API keys in an iOS app are public: why, and what to do instead

If a secret key is in your app bundle, assume every user can read it. Hiding it in a plist, an xcconfig or an obfuscated string only changes how long it takes.

By Ilyas A. Almadani · Updated October 22, 2026 · Steps checked against the vendors' official documentation, linked at the end

The short version
  1. An app is a package that runs on devices you do not control, so anything inside it can be extracted.
  2. Do not ship secret keys. Ship publishable keys that are designed to be public.
  3. Put secrets on a server and have the app call your server.
  4. If a secret already shipped, rotate it and release a fixed build.

Why a key in your app is public

An iOS app is distributed to users' devices. Strings, plist values and compiled constants travel with it. People can download an app, unpack it, and search it for text that looks like a key, and they can watch the network requests the app makes. Provider documentation says the same:

  • Stripe: "Unauthorized parties can unpack applications to search for embedded keys." For client-side use cases such as mobile apps, embed publishable keys instead.
  • Google (Gemini API): do not hardcode API keys directly in web or mobile apps, because users can extract them. Route calls through a backend.
  • Supabase: only the publishable key is appropriate in client code. Secret keys must stay out of browsers, shipped apps and source control.

Putting the key in Info.plist, a .xcconfig file, a Swift constant split into pieces, or an encoded string does not change that. These tricks slow down a casual reader but do not protect a key that grants real access or costs money.

Step 1: Sort your keys into public and secret

  • Designed to be public: Stripe publishable keys, Supabase publishable (or legacy anon) keys, and Firebase client configuration. Their safety depends on server-side rules, such as Supabase Row Level Security or Firebase Security Rules, and on key restrictions. See Firebase and Supabase keys.
  • Must never ship: Stripe secret and restricted keys, Supabase secret (or service_role) keys, OpenAI and Anthropic keys, Gemini Developer API keys, AWS secret keys, signing keys, database passwords.

Step 2: Move secrets behind a small server

The pattern is the same for every provider: the app talks to your server, and your server talks to the provider with the secret key.

  1. Create an endpoint on a server or serverless function you control (for example POST /api/chat).
  2. Store the provider key in that platform's secret store or environment variables.
  3. Authenticate the user to your server (Sign in with Apple, an account token, or similar), so strangers cannot use your endpoint as a free proxy.
  4. Rate-limit per user, and cap spending at the provider (see AI API keys: spend limits).
  5. Check inputs on the server. The app is not a trusted caller.

Apple provides App Attest (part of the DeviceCheck framework) to help your server confirm requests come from a genuine copy of your app. It raises the bar for abuse but does not turn a shipped secret into a safe one.

What about the Keychain?

The Keychain is for secrets that belong to the user or are created at runtime, such as a session token after sign-in. It does not help with a key you compile into the app for everyone, because the key still has to be in the bundle before it can be stored.

Check your Xcode project before release

SHIPSAFE includes an iOS pack for Swift and Xcode projects. On your Mac, it looks for provider keys and hard-coded credentials in project files, credentials printed with print, NSLog or os_log, and signing keys (.p8, .p12) or provisioning profiles committed to the repository. Nothing leaves your machine. Open the Mac App Store page.

If a secret key already shipped

  1. Rotate or revoke it at the provider now. Old app versions will keep working only if they do not need that key.
  2. Release a new build that calls your server instead.
  3. Check the provider's usage logs for activity you do not recognise.
  4. Consider how long old builds stay in use. A forced-update message or a server-side kill switch can help.

Do not print secrets either

Values printed to the console end up in Xcode logs, device diagnostics and sometimes crash reports. Remove debug prints that include tokens or passwords before you archive a release build. Use os.Logger for intentional diagnostics.

Frequently asked questions

Can I hide an API key in my iOS app with obfuscation?

Obfuscation slows down a casual reader but does not stop someone who wants the key, because the app must still use it at runtime and its network traffic can be observed. Keep secret keys on a server.

Is it okay to put a Stripe publishable key in my app?

Yes. Stripe documents publishable keys as safe to include in front-end code and mobile apps. Secret and restricted keys must stay on your server.

Does the iOS Keychain protect an API key that I ship in the app?

No. The Keychain protects data stored on one device at runtime. A key compiled into the app is already readable in the app package before it reaches the Keychain.

Official documentation used for this guide

Vendor interfaces change. If a menu looks different, follow the linked documentation.

More guides

See all SHIPSAFE guides

Check your project before the next release

SHIPSAFE scans a project folder on your Mac for leaked keys and risky release settings, and shows the file, the line and a suggested fix for each finding. It runs 100% locally, with no upload and no account. It is a review assistant, not a guarantee of security.

SHIPSAFE findings list showing the file, line and evidence for each issue