# NS*UsageDescription purpose strings present for all declared permissions

- **Pattern:** `ab-000467` (`app-store-privacy-data.privacy-declarations.ns-usage-descriptions-complete`)
- **Severity:** critical
- **Lifecycle:** active
- **Last modified:** 2026-04-18
- **Canonical URL:** https://auditbuffet.com/patterns/ab-000467
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-000467)

## Why it matters

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.

## Severity rationale

Critical because missing NS*UsageDescription keys cause automatic binary rejection — the app cannot be distributed on TestFlight or the App Store under any circumstance.

## Remediation

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:

```json
{
  "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.

## Detection

- **ID:** `ns-usage-descriptions-complete`
- **Severity:** `critical`
- **What to look for:** First enumerate all permission-triggering code in the project. For React Native/Expo: search for `requestPermissionsAsync`, `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".
- **Pass criteria:** Every permission used in code has a corresponding `NS*UsageDescription` key with a genuine, feature-specific explanation in Info. At least 1 implementation must be verified.plist / app.json.
- **Fail criteria:** Any permission API called in code without the corresponding `NS*UsageDescription` key; or a key is present but its value is empty, generic, or a placeholder.
- **Skip (N/A) when:** App targets Android only and has no iOS build target.
- **Detail on fail:** `"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"`
- **Remediation:** Missing or inadequate `NS*UsageDescription` keys cause automatic binary rejection — apps with missing strings cannot be installed even on TestFlight.
  1. For Expo managed workflow, add all descriptions to `app.json` under `expo.ios.infoPlist`:
     ```json
     {
       "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."
           }
         }
       }
     }
     ```
  2. For native iOS, edit `Info.plist` directly in Xcode or a text editor
  3. Write descriptions in the first person from the app's perspective, mentioning the specific feature that uses the permission

## External references

- external apple-requesting-permission — https://developer.apple.com/documentation/uikit/protecting_the_user_s_privacy/requesting_access_to_protected_resources
- gdpr Art.5(1)(b)
- gdpr Art.5(1)(c)
- ccpa §1798.100

Taxons: regulatory-conformance, privacy-consent

HTML version: https://auditbuffet.com/patterns/ab-000467
