Missing or inadequate NS*UsageDescription strings cause automatic binary rejection — apps without them cannot be installed on TestFlight, let alone the App Store. Apple enforces this at the binary validation stage before human review begins. Beyond rejection, an empty or generic string like 'We need camera access' violates GDPR Art.5(1)(b) (purpose limitation) by failing to communicate the specific feature requiring access, and CCPA §1798.100 by withholding disclosure of why personal data is collected. Every permission your code requests must be matched with a substantive, feature-specific explanation.
Critical because missing NS*UsageDescription keys cause automatic binary rejection — the app cannot be distributed on TestFlight or the App Store under any circumstance.
For Expo managed workflow, add all permission descriptions to app.json under expo.ios.infoPlist. Each description must name the specific feature — not just the data type:
{
"expo": {
"ios": {
"infoPlist": {
"NSCameraUsageDescription": "The camera lets you attach photos to your expense reports and scan receipts.",
"NSLocationWhenInUseUsageDescription": "Your location is used to find nearby merchants and pre-fill expense addresses."
}
}
}
}
For native iOS, edit Info.plist directly in Xcode. Write descriptions in first person from the app's perspective. Audit every permission API call in the codebase and verify a matching key exists before every submission.
app-store-privacy-data.privacy-declarations.ns-usage-descriptions-completecriticalrequestPermissionsAsync, requestCameraPermissionsAsync, requestMicrophonePermissionsAsync, requestForegroundPermissionsAsync, requestBackgroundPermissionsAsync, requestContactsPermissionsAsync, requestCalendarPermissionsAsync, PermissionsAndroid.request, AVCaptureDevice.requestAccess, UNUserNotificationCenter.requestAuthorization, ATTrackingManager.requestTrackingAuthorization. For Flutter: permission_handler plugin calls (Permission.camera.request(), Permission.location.request(), etc.). For native Swift: CLLocationManager.requestWhenInUseAuthorization(), PHPhotoLibrary.requestAuthorization(), AVCaptureDevice.requestAccess(for:), CNContactStore().requestAccess(for:). Then verify each has the corresponding NS*UsageDescription key in Info.plist (or app.json ios.infoPlist). The string value must be substantive: it must explain WHY the permission is needed in terms the user understands (not "We need camera access" — instead "We need camera access to let you scan receipts and attach photos to expenses"). Check for: empty strings; generic strings that don't mention the specific feature; placeholder strings like "Permission required" or "Used for app functionality".NS*UsageDescription key with a genuine, feature-specific explanation in Info. At least 1 implementation must be verified.plist / app.json.NS*UsageDescription key; or a key is present but its value is empty, generic, or a placeholder."Camera permission requested in src/screens/ProfileScreen.tsx but NSCameraUsageDescription is missing from Info.plist" or "NSLocationWhenInUseUsageDescription is set to 'We need your location' — too generic; Apple requires a specific feature explanation"NS*UsageDescription keys cause automatic binary rejection — apps with missing strings cannot be installed even on TestFlight.
app.json under expo.ios.infoPlist:
{
"expo": {
"ios": {
"infoPlist": {
"NSCameraUsageDescription": "The camera lets you attach photos to your expense reports and scan receipts.",
"NSLocationWhenInUseUsageDescription": "Your location is used to find nearby merchants and pre-fill expense addresses."
}
}
}
}
Info.plist directly in Xcode or a text editor