Privacy

    How do I fix a missing Camera or Photos Purpose String?

    App Store Connect in 2026 showing an ITMS-90683 missing Purpose String rejection email next to an Xcode Info.plist editor with NSCameraUsageDescription and NSPhotoLibraryUsageDescription keys being added

    You uploaded the build, processing finished, and instead of Waiting for Review you got an email from App Store Connect with error ITMS-90683: a missing Purpose String for the camera or photo library. For a developer shipping from Xcode, Expo, or FlutterFlow, the confusing part is that the rejection often names a permission you do not remember adding.

    Short answer

    ITMS-90683 means your build can reach the camera or photo library, but Info.plist has no usage-description key explaining why. Add NSCameraUsageDescription for the camera and NSPhotoLibraryUsageDescription for the photo library, each with a specific reason the user reads in the permission prompt. Apple has required a purpose string for every app that accesses protected data since spring 2019. Because the keys live in Info.plist, the fix ships in a new build, not a metadata edit.

    What you should know

    • ITMS-90683 is a binary check, not a content review: it fires at upload or during processing, before a human opens the app.
    • Each protected resource has its own key: camera, photo library, microphone, and location use different Info.plist keys.
    • A bundled SDK can trigger it: a library that references a camera or photo API requires the key even when your own code never calls it.
    • The string has to be specific: a vague or empty reason can be rejected separately under Guideline 5.1.1.
    • The fix needs a new build: Info.plist is compiled into the binary, so editing App Store metadata cannot resolve it.

    Which Info.plist key does each permission need?

    The short answer is that the key name has to match the exact resource the build can touch. App Review reads Info.plist for a usage-description string keyed to each protected API your binary references. The table maps the common camera and photo keys.

    ResourceInfo.plist keyWhen it is required
    CameraNSCameraUsageDescriptionThe build can capture photo or video
    Photo library (read or write)NSPhotoLibraryUsageDescriptionThe build can read from or save to the library
    Photo library (add only)NSPhotoLibraryAddUsageDescriptionThe build only saves images and never reads them
    MicrophoneNSMicrophoneUsageDescriptionThe build can record audio, including video with sound

    The add-only key is the one most developers miss. An app that only saves a generated image to the camera roll still needs NSPhotoLibraryAddUsageDescription, and omitting it produces the same ITMS-90683 rejection as a missing camera key.

    Why is my app flagged for a permission it never uses?

    Because the requirement is triggered by the API the binary can reach, not by the feature you built. A third-party SDK compiled into your app can reference a camera or photo library API, and Apple's static check sees that reference at the binary level. FlutterFlow's guidance on this rejection states plainly that an external library may reference these APIs even when your app does not, and that a purpose string is still required. The practical consequence is that an analytics, chat, or image-picker SDK can force you to declare a key for a feature your users never see. The limit here is that you often cannot tell from your own source which dependency pulled the API in, which is why scanning the compiled build is more reliable than rereading your code.

    How do you add the Purpose String in a native, Expo, or FlutterFlow project?

    The idea is the same in every stack: put the key and a human-readable reason into the final Info.plist, then rebuild. The place you edit differs.

    In a native Xcode project, add the keys to Info.plist directly:

    <key>NSCameraUsageDescription</key>
    <string>This app uses the camera so you can take and attach photos.</string>
    <key>NSPhotoLibraryUsageDescription</key>
    <string>This app accesses your photos so you can upload them to your profile.</string>
    

    In an Expo project, declare them in app.json under ios.infoPlist, and Expo writes them into Info.plist during prebuild:

    {
      "expo": {
        "ios": {
          "infoPlist": {
            "NSCameraUsageDescription": "This app uses the camera so you can take and attach photos.",
            "NSPhotoLibraryUsageDescription": "This app accesses your photos so you can upload them."
          }
        }
      }
    }
    

    In FlutterFlow, open the project Permissions settings, enable the camera and photos permissions, and write a clear description for each. FlutterFlow injects the matching keys into Info.plist when it builds the iOS bundle, so the edit happens in the builder rather than in a file you open by hand.

    Does fixing a Purpose String need a new build or just a metadata reply?

    It needs a new build. Info.plist is compiled into the app bundle, so the only way to change a usage-description string that App Store Connect validates is to rebuild and re-upload. That returns the submission to Waiting for Review for a full review, the same reset behavior any new binary triggers. A metadata reply cannot clear ITMS-90683, because the missing data sits inside the binary and not in the App Store listing. The useful habit is to batch this fix with any other binary-level changes, rather than uploading once per key and restarting review each time.

    What to watch out for

    Two traps show up after the first fix. The first is the vague string. A key that only says "This app needs camera access" can clear ITMS-90683 and then draw a separate rejection under App Store Review Guideline 5.1.1, which asks for a specific, user-facing reason; name the feature the user gets when they allow it. The second is the assumption that you should declare a key only for features you wrote. The check is driven by what the binary can reach, including SDK code, so map every protected API your build actually links, then declare exactly those. Mapping them by hand is error-prone, which is the argument for an external read of the build. For builders who want a list of every permission an APK, AAB, or IPA actually requests before submitting, PTKD.com (https://ptkd.com) is one of the platforms focused on pre-submission scanning aligned with OWASP MASVS.

    What to take away

    • ITMS-90683 means a usage-description key is missing for a camera or photo API your binary can reach; add the matching NS...UsageDescription key and rebuild.
    • The trigger is the API the build links, not the feature you wrote, so a bundled SDK can require a key for a permission your users never see.
    • Write a specific reason in each string, because a vague one can pass the binary check and still be rejected under Guideline 5.1.1.
    • The keys live in the binary, so the fix always rides a new build; a pre-submission scan such as PTKD.com that lists every requested permission helps you declare exactly the right keys the first time.
    • #itms-90683
    • #purpose-string
    • #nscamerausagedescription
    • #nsphotolibraryusagedescription
    • #info-plist
    • #app-store-connect
    • #ios

    Frequently asked questions

    What is error ITMS-90683 exactly?
    ITMS-90683 is a binary-validation rejection that means your app references a protected API, such as the camera or photo library, without the matching usage-description key in Info.plist. It fires during upload or processing in App Store Connect, before a human reviewer sees the app. The fix is to add the correct NS...UsageDescription key with a clear reason and upload a new build.
    Which key do I need for the photo library, read versus add-only?
    Use NSPhotoLibraryUsageDescription when the build can read from or write to the photo library, and NSPhotoLibraryAddUsageDescription when it only saves images and never reads them. Many apps that just save a generated image miss the add-only key and get flagged. If you are unsure which access your SDKs use, declaring the broader read and write key with an honest reason is the safer default.
    Why is Apple asking for camera access when my app has no camera feature?
    A third-party SDK compiled into your binary most likely references a camera API. App Review's static check sees that reference at the binary level, so it requires the purpose string even though your own code never opens the camera. FlutterFlow documents the same behavior for libraries that pull in these APIs. Identify the SDK, then either declare the key honestly or remove the dependency.
    Can I just add every possible permission key to be safe?
    It is not a safe shortcut. Declaring keys for permissions your app does not need can draw a separate rejection, because a reviewer may question why the app requests access it never uses, which falls under Guideline 5.1.1. Declare the keys that match the APIs your build actually links, no more. A scan that lists the real permission set is more reliable than guessing.
    Does fixing the Purpose String require a new build?
    Yes. Info.plist is compiled into the app bundle, so a usage-description string can only change with a rebuild and re-upload. That sends the submission back to Waiting for Review for a full review. You cannot resolve ITMS-90683 with a metadata reply, because the missing data is inside the binary rather than in the App Store listing.

    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