App Store

    How do I fix the 'Missing Purpose String' rejection in 2026?

    Xcode editor showing an Info.plist file with NSLocationWhenInUseUsageDescription and NSLocationAlwaysAndWhenInUseUsageDescription keys filled in with user-facing purpose strings for a delivery app

    If App Store Connect bounced your upload with a red ITMS-90683 banner that mentions a missing purpose string, the build did not fail for a code-signing or metadata issue. It failed because Apple's automated check found a call to a privacy-sensitive system API without a matching reason in Info.plist. The fix is one or two short XML entries; the trap is figuring out which SDK pulled the API in.

    Short answer

    ITMS-90683 means your binary calls a sensitive system API, almost always location, camera, contacts, or motion, and Info.plist does not declare why. Add the matching NSLocation*UsageDescription, NSCameraUsageDescription, or NSContactsUsageDescription key with a short human-readable reason, rebuild, and resubmit. According to Apple's documentation for requesting Core Location authorization, apps that ever call requestAlwaysAuthorization must include both NSLocationWhenInUseUsageDescription and NSLocationAlwaysAndWhenInUseUsageDescription, or App Store Connect rejects the upload at submission time.

    What you should know

    • The check runs at upload time, not at runtime. It is symbol-based, so the missing key is flagged whether or not your app reaches that API in practice.
    • The key list is fixed. Apple publishes every NSXxxUsageDescription string in the Information Property List reference. Invented keys do not satisfy the check.
    • Location is the most frequent offender. requestAlwaysAuthorization needs both NSLocationWhenInUseUsageDescription and NSLocationAlwaysAndWhenInUseUsageDescription paired together.
    • Third-party SDKs are the usual cause. Stripe card-scan, OneSignal, Firebase Analytics, AppsFlyer, and Branch have all triggered location warnings in apps that never call Core Location directly.
    • The string is user-facing. Apple shows your exact text in the iOS permission dialog. A meaningless placeholder ("App needs location") can pass the upload check and still trigger a Guideline 5.1.1 rejection from the human reviewer.

    What does the 'Missing Purpose String' message actually mean?

    The short answer is that ITMS-90683 is the build-time form of a permission audit Apple runs against the linker output of your IPA. The check looks for symbols in your binary that map to APIs guarded by a permission prompt: location, camera, microphone, contacts, photo library, calendar, reminders, Bluetooth, motion, speech recognition, Face ID, HealthKit, HomeKit, Siri, App Tracking Transparency. Each one has a fixed Info.plist key. If the symbol appears in the linked binary and the matching key is absent, the upload is rejected with ITMS-90683 and the category name.

    The message itself has a stable shape: "Missing purpose string in Info.plist. Your app's code references one or more APIs that access sensitive user data, or the app has one or more entitlements that permit such access. The Info.plist file should contain a NSLocationAlwaysAndWhenInUseUsageDescription key with a user-facing purpose string explaining clearly and completely why your app needs the data." That last sentence varies by category and is the only reliable signal of which key to add. According to Apple's reference page for NSLocationAlwaysAndWhenInUseUsageDescription, the string is mandatory whenever any code path in the binary can request always authorization, even one that the user can never reach in normal use.

    The check is automated and lives inside App Store Connect's upload pipeline. It runs before TestFlight processing, and it ignores Info.plist entries that exist only in build variants you did not submit. Submitting the Release configuration matters; the Debug Info.plist does not count for the upload that gets reviewed.

    Which Info.plist keys does iOS require for location access today?

    Two keys for location-always, one for location-while-in-use. iOS 11 and later require NSLocationWhenInUseUsageDescription for any foreground location request, paired with NSLocationAlwaysAndWhenInUseUsageDescription whenever the app ever escalates to always authorization. NSLocationAlwaysUsageDescription is the iOS 10 and earlier name; modern builds rarely need it, but the App Store Connect check still flags it for builds that target older deployment versions or that ship a Core Location SDK compiled against an older base SDK.

    Apple's requesting authorization to use location services page sets the rule clearly: if your code calls requestWhenInUseAuthorization(), include NSLocationWhenInUseUsageDescription. If your code ever calls requestAlwaysAuthorization(), include both NSLocationWhenInUseUsageDescription and NSLocationAlwaysAndWhenInUseUsageDescription. The system shows the when-in-use string first; the always string appears later if and when the prompt escalates.

    Permission scopeRequired Info.plist key(s)Applies to
    Foreground onlyNSLocationWhenInUseUsageDescriptioniOS 8 and later
    Always (modern)NSLocationWhenInUseUsageDescription plus NSLocationAlwaysAndWhenInUseUsageDescriptioniOS 11 and later
    Always (legacy)NSLocationAlwaysUsageDescriptioniOS 10 and earlier
    Background updatesOne of the above, plus the Background Modes capability with Location updates checkedAll versions

    How do you find the SDK that triggered the missing purpose string?

    The trap with ITMS-90683 is that grep over your source tree usually returns nothing. The call lives inside a compiled framework binary, often shipped as a static archive or an XCFramework with no source. Three commands cover most cases.

    First, run grep -r "NSLocation" Pods/ (or node_modules for React Native, or your Plugins folder for Flutter) to find any framework that mentions a location key in its bundled Info.plist or its source. Many SDKs document their permission needs by shipping an example Info.plist snippet inside the package.

    Second, run nm -gU on each .framework or .a inside the build artifact and pipe through grep -i location. The Core Location symbols that App Store Connect checks for include _CLLocationManager, _requestAlwaysAuthorization, _requestWhenInUseAuthorization, and _startMonitoringSignificantLocationChanges. If any of these appear in a third-party binary, your Info.plist needs the matching key, whether or not your own code calls them.

    Third, search the issue trackers. The pattern is well documented: Firebase Analytics (pre-10.x), OneSignal (specifically on Flutter and Cordova), Branch, AppsFlyer, and the Stripe iOS SDK (because the card-scanning module links Core Location through the scanner) have each generated ITMS-90683 reports for apps that did not call Core Location directly. Pinning these SDKs to a version that ships a privacy manifest declaring the access often clears the warning at the next upload.

    What does an acceptable purpose string look like to App Review?

    Apple's App Review Guideline 5.1.1 on Data Collection and Storage requires the string to clearly and completely describe the data use. In practice, that means three things. The sentence is a complete English sentence. It names the feature that needs the data. It names the data itself ("your location", "your camera"). Vague strings like "Permission required" or copies of the key name fail human review under 5.1.1 even when the build passes ITMS-90683 at upload.

    A working example for a delivery app:

    <key>NSLocationWhenInUseUsageDescription</key>
    <string>This app uses your location to show couriers near you and route your order. Location data leaves the device only when you place an order.</string>
    

    That string passes both checks: the upload scanner sees the key exists, and the reviewer sees a sentence that maps to a feature in the binary. Builders that auto-generate the string ("App requires access to location") often pass the upload check and then catch a 5.1.1 rejection at human review. Rewrite those strings before submission.

    Why does the same build that passed last month suddenly fail?

    Two reasons, both visible on the developer forums. First, the long-running Apple Developer Forums thread on ITMS-90683 shows that Apple's symbol scanner updates without announcement. A symbol that was not flagged in 2023 can be flagged in 2026 once Apple adds it to the watched list. Second, dependency updates pull new code paths. A minor-version bump of Firebase or Branch can introduce a Core Location call that did not exist in the previous version. Lock dependencies, diff the framework binaries between releases, and run nm against the artifact before each App Store Connect upload.

    A third reason matters for no-code builders. FlutterFlow, Bubble, Thunkable, and Adalo each ship their own iOS shells, and each release bundles a different set of native plugins. A build exported from FlutterFlow in May 2026 may include a location plugin that the same project did not include in February 2026, and ITMS-90683 fires only on the new build. Open the generated Info.plist from the exported project and confirm which keys are declared before resubmitting.

    What to watch out for

    The first trap is declaring every key to be safe. Apple's Guideline 5.1.1 reviewer flags purpose strings for APIs the app does not actually use, because the description is then false. Match each key to a real code path. If a dependency is the only reason a key exists, the string should describe the dependency feature ("Card scanning checks your card photo, which can access location metadata"), not invent a feature your app does not have.

    The second trap is shipping a placeholder string. The text is user-facing on the iOS permission prompt. Strings like "Test", "Required", or "App needs location" are accepted by the upload scanner and rejected by the human reviewer. Rewrite them to one or two complete sentences naming the feature and the data.

    The third trap is targeting an older deployment version. Builds with iOS 10 still in the deployment target may need NSLocationAlwaysUsageDescription as well as the modern keys. Raise the deployment target to iOS 13 or later if there is no specific reason to support iOS 10. The OWASP MASVS privacy controls also recommend declaring only the minimum data set the app actually uses, not a superset that exists "just in case".

    For builders who want an external automated read of their compiled IPA's Info.plist and permission usage before they submit to App Store Connect, PTKD.com (https://ptkd.com) is one of the platforms focused specifically on pre-submission scanning aligned with OWASP MASVS for no-code and vibe-coded apps. The scanner flags missing purpose strings, missing privacy manifest entries, and SDK-induced API access that the developer may not realize the binary contains. A scan is faster than a TestFlight round-trip when the goal is to confirm Info.plist coverage before the next upload attempt.

    Key takeaways

    • ITMS-90683 is a symbol-based check. Adding the right Info.plist key, not changing source code, is the fix in almost every case.
    • For Core Location, ship NSLocationWhenInUseUsageDescription as a minimum; add NSLocationAlwaysAndWhenInUseUsageDescription whenever any code path in the binary can request always authorization.
    • The cause is usually a third-party SDK that links Core Location internally. Pin SDK versions, run nm on framework binaries, and search issue trackers before guessing.
    • The user-facing string is reviewed twice. The upload scanner only checks that the key exists; the human reviewer under Guideline 5.1.1 checks that the sentence is honest.
    • Some teams scan compiled IPAs through external platforms like PTKD.com (https://ptkd.com) before each App Store Connect upload to catch missing permission descriptions, missing privacy manifest entries, and SDK-pulled APIs the developer did not realize are in the binary.
    • #itms-90683
    • #info.plist
    • #purpose string
    • #nslocationalwaysandwheninuseusagedescription
    • #core location
    • #app store connect
    • #ios
    • #guideline 5.1.1

    Frequently asked questions

    Can I just add every NSLocation* key to be safe?
    That works for the upload scanner, since App Store Connect only verifies the key exists. It often fails the human review under Guideline 5.1.1 because the purpose strings describe features the app does not have. The reviewer reads each string against the live app, and a false claim of background location use can trigger a second rejection. Declare only the keys for APIs your binary actually reaches.
    Does NSLocationAlwaysUsageDescription still matter in 2026?
    For builds that target iOS 11 and later, the modern pair (NSLocationWhenInUseUsageDescription plus NSLocationAlwaysAndWhenInUseUsageDescription) is sufficient. NSLocationAlwaysUsageDescription matters only when the deployment target includes iOS 10 or earlier, or when a third-party static library was compiled with the older SDK and still references the old key internally. Most apps in 2026 can omit it; verify by checking the deployment target in the Xcode build settings.
    Why does Apple flag location when my app only uses Stripe for payments?
    The Stripe iOS SDK includes a card-scanning module that touches Core Location under specific code paths. App Store Connect scans the linked binary, not just the code paths you actively call, so the location symbols appear and ITMS-90683 fires. Pin Stripe to a version that ships a privacy manifest declaring the access, add NSLocationWhenInUseUsageDescription with a Stripe-specific reason, and the upload passes.
    How do I fix this in a FlutterFlow export?
    Open the exported Xcode project, locate Runner/Info.plist, and add the missing NSLocation*UsageDescription keys with real explanations. FlutterFlow does not always include them in the export, especially when a plugin added at a later step does not surface a permission field in the visual editor. Re-archive from Xcode and upload. If the project gets regenerated, save the keys in a Dart-side build script so they survive the next export.
    Will switching from requestAlwaysAuthorization to requestWhenInUseAuthorization remove ITMS-90683?
    Only if no SDK in the binary still calls the always API. The check is symbol-based, so removing the call from your own code does not remove the symbol if a linked framework still imports it. Run nm against each linked framework, find any framework that contains _requestAlwaysAuthorization, and either replace the framework or keep NSLocationAlwaysAndWhenInUseUsageDescription with an honest description of why the dependency needs it.

    Keep reading

    Scan your app in minutes

    Upload an APK, AAB, or IPA. PTKD returns an OWASP-aligned report with copy-paste fixes.

    Try PTKD free