Key takeaways
- ITMS-90683 means your app, or a library inside it, references an API that accesses sensitive user data and Info.plist has no matching purpose string. The email names the key it wants, such as NSCameraUsageDescription.
- Apple says you are responsible for all access of protected resources, including access by external SDKs and libraries. A plugin can trigger the email even if your app never uses that feature.
- You have two fixes. Add an accurate purpose string for the key, or remove the access: with permission_handler, set the unused PERMISSION_ macros to 0 in the Podfile and delete that permission's key from Info.plist.
- Apple says each purpose string must be accurate, meaningful and specific, and App Review guideline 5.1.1 asks that purpose strings clearly and completely describe your use of the data. A vague placeholder is a risk.
- After you change the Podfile or Info.plist, run pod install, rebuild, and upload a new build.
Pricing an app? Try the free app cost calculator or get a range for your project in the instant quote.
If App Store Connect emailed you ITMS-90683, your app's code references an API that accesses sensitive user data, and Info.plist has no purpose string for it. The email names the missing key, for example NSCameraUsageDescription or NSPhotoLibraryUsageDescription. In a Flutter app the trigger is often a plugin, not your own code. The fix is to add an accurate purpose string for features you really ship, or to remove the access so the key is no longer needed.
The facts below come from Apple's developer documentation, the App Review Guidelines, the plugin READMEs and the GitHub issues listed in the sources. I checked each one on October 18, 2026.
Disclosure: App369 is my studio. I build and fix Android and iOS apps, mostly in Flutter, and the last section mentions what I sell. Apple doesn't pay me for anything on this page.
What the email says
The email heading is "ITMS-90683: Missing Purpose String in Info.plist". A Flutter issue (113603) quotes one such email, posted by an image_picker user. It includes this sentence: "While your app might not use these APIs, a purpose string is still required." Your wording may differ. Another issue (permission_handler 1543) quotes a version that also mentions entitlements, so read your own email for the key name.
That key name is the part you need. These are the keys I have seen in the sources for this page:
| Key in the email | What it is for | Where I saw it |
|---|---|---|
| NSCameraUsageDescription | Required if your app uses APIs that access the device's camera | Apple |
| NSPhotoLibraryUsageDescription | Required if your app uses APIs with read or write access to the photo library. If your app only adds assets and doesn't read them, Apple points to a different key | Apple |
| NSMicrophoneUsageDescription, NSContactsUsageDescription, NSLocationWhenInUseUsageDescription, NSBluetoothAlwaysUsageDescription | Microphone, contacts, location while in use, Bluetooth | Listed in the permission_handler README table of permissions and Info.plist keys |
Why a Flutter app gets it for features it doesn't use
Apple's documentation says it plainly. App Review checks for the use of protected resources and rejects apps that contain code accessing them without a purpose string. It also says that external libraries or SDKs may reference APIs that require a purpose string, that a purpose string is still necessary even if your app doesn't use those APIs, and that you are responsible for all access of protected resources, including external SDK and library access. Source: Apple, Requesting access to protected resources.
Two common Flutter cases:
- image_picker. Its README says to add NSPhotoLibraryUsageDescription, NSCameraUsageDescription and, if you intend to record videos, NSMicrophoneUsageDescription. It adds that the photo library permission won't be requested if you always pass false for requestFullMetadata, but that "App Store policy requires including the plist entry." That matches the email quoted in issue 113603.
- permission_handler. The README says the plugin uses macros to control whether a permission is enabled. If a permission's code is compiled in, the app references that API, and Apple's page says App Review rejects code that accesses a protected resource without a purpose string.
Flutter's older issue 21941 shows this isn't new. In 2018 a developer got the same kind of email for contacts, calendars, Apple Music, motion and speech recognition keys that their app didn't use.
Fix 1: add accurate purpose strings
Do this for features your app really ships. Apple's rules for a purpose string:
- It isn't blank and isn't only whitespace.
- It is shorter than 4,000 bytes. Typical strings are one complete sentence.
- It is accurate, meaningful and specific about why the app needs the resource.
- The same rules apply to localized strings.
Add the key to ios/Runner/Info.plist. Here is the shape. These strings are examples only. Rewrite each one to say what your own app does with the data:
<key>NSCameraUsageDescription</key>
<string>EXAMPLE: Rewrite this. The camera lets you take a photo to attach to a job report.</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>EXAMPLE: Rewrite this. Your photo library lets you choose an existing photo to attach to a job report.</string>
Apple's page says purpose strings help people make an informed decision, and that explaining accurately and concisely, typically in one complete sentence, improves the chances they grant access.
My advice: don't paste a generic sentence into every key just to clear the email. App Review guideline 5.1.1(ii) says "Ensure your purpose strings clearly and completely describe your use of the data." A string that describes a feature your app doesn't have is a new problem.
Fix 2: remove the access you don't use
Guideline 5.1.1(iii) says apps should only request access to data relevant to the core functionality of the app. Apple's page, in its example of an App Review message, says to either provide a purpose string or remove the code that's accessing the resource.
With permission_handler on CocoaPods, the README gives this procedure. In your Podfile's post_install block, set the macro of each permission you don't need to 0, and delete that same permission's usage description from Info.plist. The README says to use PERMISSION_CAMERA=0 instead of PERMISSION_CAMERA=1, for example. Trimmed shape:
post_install do |installer|
installer.pods_project.targets.each do |target|
flutter_additional_ios_build_settings(target)
target.build_configurations.each do |config|
config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= [
'$(inherited)',
# permissions you use: keep the macro, set to 1, for example:
'PERMISSION_PHOTOS=1',
# permissions you don't use: set to 0, or leave the macro out
'PERMISSION_CAMERA=0',
'PERMISSION_CONTACTS=0',
'PERMISSION_MICROPHONE=0',
]
end
end
end
A macro set to 1 compiles that permission in. A macro set to 0, or left out, leaves it out. Keep 1 only for permissions your app really uses. The ||= line is copied from the README.
Use the full list from the README. Its table maps each Permission to its Info.plist key and macro. Location has two macros: PERMISSION_LOCATION covers when-in-use and always, and the README says an app that only needs when-in-use should specify only PERMISSION_LOCATION_WHENINUSE.
The README also says that permission_handler by default excludes all permissions since version 8.0.0, and developers only enable those the app needs. If your Podfile came from an old template with every macro set to 1, that is a place to look.
If you build with Swift Package Manager, the README describes a different mechanism. It finds your Info.plist through the INFOPLIST_FILE setting and compiles in the permissions whose keys it finds there, so a single-flavor app needs no extra configuration. It also says that for apps with flavors, a permission declared in one plist can be compiled into every build, which it calls grounds for App Store rejection (ITMS-90683). It documents a permission_handler.yaml file to give each flavor its own plist. Issue 1558 shows a related report about location. Check the current README for your plugin version.
Rebuild and re-upload
- Edit
ios/Runner/Info.plistand, for CocoaPods, the Podfile. - From the
iosfolder, runpod install. The README's last step for the Podfile route is "Clean & Rebuild". - Build a new archive. The upload needs a new build number.
- Upload again and wait for the processing email.
My advice: if your upload also fails with a different code, read it separately. For SDK version errors see the ITMS-90725 guide. For privacy manifest errors see the ITMS-91053 and ITMS-91061 guide. If a Flutter upgrade broke the iOS build itself, see the iOS build-failed-after-upgrade guide.
What happens if the string is missing at runtime
Apple's page says to always provide a valid purpose string in the Signing and Capabilities editor if your app uses a protected resource. It continues: "If you don't, attempts to access the resource fail, and might cause your app to crash." So a missing string can hurt users as well as your upload. Add the string for any feature you ship.
Need a hand?
If App Store Connect keeps flagging missing purpose strings, or App Review rejects the wording, the App Fix Ticket covers it in two fixed-price steps. A $450 Diagnosis gives you the root cause with evidence and a fixed repair quote within 3 business days, credited toward the fix. If you go ahead, the $1,200 Fix takes 5 business days and comes as a pull request. Or send me a short description.
Frequently asked questions
- What does ITMS-90683 Missing purpose string mean?
- It means the app's code references an API that accesses sensitive user data, and the Info.plist has no purpose string for it. Apple's documentation says App Review rejects apps that contain code accessing protected resources without a purpose string. The email names the key it expects.
- Why do I get it for a camera or contacts feature I do not use?
- Apple says external libraries and SDKs may reference APIs that require a purpose string, and that a purpose string is still necessary even if your app does not use those APIs. In Flutter, a plugin such as permission_handler or image_picker can link those APIs.
- Can I just write any text in the purpose string?
- No. Apple says a purpose string must not be blank and must be accurate, meaningful and specific about why the app needs the resource. Write what your app really does with the data, and only add keys for features you ship.
- How do I turn off unused permissions in permission_handler?
- For CocoaPods, the plugin README says to set the macro to 0 in the GCC_PREPROCESSOR_DEFINITIONS list in your Podfile, for example PERMISSION_CAMERA=0, and delete that permission's usage description from Info.plist. Then clean and rebuild.
- What happens if the purpose string is missing at runtime?
- Apple says that if you do not provide a valid purpose string for a protected resource your app uses, attempts to access the resource fail and might cause your app to crash.
Sources
- Apple Developer, Requesting access to protected resources
- Apple Developer, NSCameraUsageDescription
- Apple Developer, NSPhotoLibraryUsageDescription
- Apple Developer, App Review Guidelines (5.1.1 Data Collection and Storage)
- Baseflow on GitHub, permission_handler README (iOS setup, Podfile macros, Swift Package Manager)
- flutter/packages on GitHub, image_picker README (iOS setup)
- Flutter on GitHub, issue 113603 (ITMS-90683 email text for NSPhotoLibraryUsageDescription with image_picker)
- Flutter on GitHub, issue 21941 (earlier Apple email text, keys for features the app did not use)
- permission_handler on GitHub, issue 1543 (ITMS-90683 email for a location key)
- permission_handler on GitHub, issue 1558 (location permission and ITMS-90683 with Swift Package Manager)
Related Resources
Related guides
ITMS-91053 and ITMS-91061: Fix Privacy Manifest Emails
App Store Connect emails ITMS-91053 (missing API declaration) and ITMS-91061 (missing privacy manifest) mean your app or an SDK in it lacks a PrivacyInfo.xcprivacy. The dates, the five required reason API categories, how to find the culprit and how to fix a Flutter or native iOS app, from Apple's and Flutter's sources checked October 18, 2026.
Read moreITMS-90725 SDK Version Issue: Fix in Flutter (Xcode 26)
ITMS-90725 SDK version issue means App Store Connect saw an app built with an older SDK than it now accepts. What Apple's upcoming requirements page says about Xcode 26 today, and how to fix it in a Flutter app, from Apple's, Flutter's and GitHub's sources checked October 18, 2026.
Read moreFlutter iOS Build Failed After an Upgrade? Fix CocoaPods and Xcode Errors (2026)
Flutter iOS build failing after upgrading Flutter, FlutterFire or Xcode? How to read the real error, then fix CocoaPods, deployment target and plugin version mismatches.
Read more