App Store · 4 min read

ITMS-90683: Missing purpose string in Info.plist.

Apple says a usage description is missing. You never asked for that permission. Your plugins did. Here is how to get Info.plist right and keep it right.

What you see

You upload a build and Apple emails you ITMS-90683. It says a purpose string is missing. Often it is NSLocationAlwaysAndWhenInUseUsageDescription, even though your app only uses location while it is open.

Or the problem shows up on a device. The app crashes the first time it asks for location. Or a camera plugin errors out and App Review rejects the build under Guideline 2.1(a).

Why it happens

iOS needs a usage description string, the NS...UsageDescription keys, for every protected feature an app can request. Two things trip people up.

Plugins reference APIs you never call. Capacitor geolocation plugins reference the Always location API inside their binary. Apple scans the binary, sees the reference, and wants the matching purpose string, even if your code only asks for While Using.

A missing string is a crash, not a warning. If the app requests a permission without its string, iOS kills the app. A missing NSLocationWhenInUseUsageDescription crashes the location prompt. Some plugins also need strings you think are unused. Removing an "orphaned" NSPhotoLibraryAddUsageDescription once broke a camera plugin.

The opposite problem exists too. Purpose strings for features you do not use, like the microphone or photo saving, draw review questions and can contradict your privacy policy.

The fix, step by step

Step 1. List your plugins. Open package.json and write down every native plugin. For each one, read its install docs and note the Info.plist keys it requires.

Step 2. Add both location keys if you use any location plugin. Use honest wording that says what the app does with location:

<key>NSLocationWhenInUseUsageDescription</key>
<string>Your location is used to show nearby jobs while the app is open.</string>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>Your location is used to show nearby jobs while the app is open.</string>

Step 3. Add every string your camera and photo plugins can trigger. For a typical camera plugin that means:

NSCameraUsageDescription
NSPhotoLibraryUsageDescription
NSPhotoLibraryAddUsageDescription

Step 4. Remove only what nothing uses. Go through the rest of your keys. For each one, point to the line of code or the plugin that reads it. If you cannot, and no plugin docs mention it, remove it. Template and copied config often leave NSMicrophoneUsageDescription behind. Do the same on Android and remove unused permissions such as RECORD_AUDIO from AndroidManifest.xml.

In Expo, these keys live in app.json under ios.infoPlist, and Android permissions under android.permissions. Edit them there, then rebuild.

Step 5. Add a CI check. Make the build fail when a required key is missing, so this never reaches Apple again:

PLIST=ios/App/App/Info.plist
for key in NSLocationWhenInUseUsageDescription \
           NSLocationAlwaysAndWhenInUseUsageDescription \
           NSCameraUsageDescription; do
  grep -q "<key>$key</key>" "$PLIST" || { echo "Missing $key"; exit 1; }
done

Adjust the list to match your plugins. Then rebuild and upload.

Step 6. Test the prompts on a real device. Trigger every permission the app can ask for. If any prompt crashes the app, a string is still missing.

How to prevent it

Related traps

Field Notes

This fix is one of 228 in Field Notes: What Broke and How We Fixed It.

Every entry follows the same pattern: the symptom you see, the real cause, the fix that worked, and the rule that stops it coming back. App Store, Google Play, Capacitor, Netlify, Supabase, Stripe, n8n and more.

Get Field Notes

Building a lot? The Builder Bundle has Field Notes plus the launch playbook, the prompt book and the Claude Code skills kit. See the bundle.

Related fixes

Keep reading.