9.9 — Privacy, Permissions & Data Minimization

Opening scenario

App Store Review rejects your submission with: “Your app uses one or more APIs that access user data without including the required Privacy Manifest entries.” Or: “Your NSLocationAlwaysAndWhenInUseUsageDescription string does not adequately describe why your app needs the user’s location.” Privacy enforcement on iOS has moved from polite suggestion to hard gate. This chapter is the playbook for shipping a compliant app — and for being a good steward of user data, which is mostly the same thing.

Context — the iOS privacy stack

LayerWhat it doesRequired since
Usage description stringsHuman-readable reason at the OS-level promptiOS 6+
App Tracking Transparency (ATT)Explicit consent for cross-app trackingiOS 14.5
Privacy Nutrition LabelsDisclosed in App Store listingDec 2020
App Privacy ReportUser-facing diagnostic showing app data accessiOS 15.2
Required Reason API declarationsJustify use of specific APIs (e.g., file timestamps)Xcode 15 (May 2024)
PrivacyInfo.xcprivacyManifest of tracked domains + reason APIsXcode 15 (May 2024)

Missing or inaccurate entries lead to App Store Review rejections. Apple has tightened enforcement annually; 2024–2025 reviews catch what passed in 2022.

Usage description strings (Info.plist)

Every permission-gated API requires a corresponding Info.plist string:

PermissionKey
CameraNSCameraUsageDescription
MicrophoneNSMicrophoneUsageDescription
Photo Library (read)NSPhotoLibraryUsageDescription
Photo Library (add only)NSPhotoLibraryAddUsageDescription
Location (always + in-use)NSLocationAlwaysAndWhenInUseUsageDescription
Location (when-in-use)NSLocationWhenInUseUsageDescription
ContactsNSContactsUsageDescription
CalendarNSCalendarsUsageDescription
RemindersNSRemindersUsageDescription
BluetoothNSBluetoothAlwaysUsageDescription
Local NetworkNSLocalNetworkUsageDescription
Tracking (ATT)NSUserTrackingUsageDescription
Face IDNSFaceIDUsageDescription
Speech RecognitionNSSpeechRecognitionUsageDescription

Quality matters. Vague strings like “We need this for app functionality” get rejected. Strong strings explain the concrete user-facing benefit: “Camera access lets you scan business cards and add contacts automatically.” Apple’s review explicitly judges this.

App Tracking Transparency

If your app uses the IDFA (Identifier for Advertisers) or shares identifiers with third parties for tracking across apps, you must prompt:

import AppTrackingTransparency
import AdSupport

func requestTracking() async {
    guard ATTrackingManager.trackingAuthorizationStatus == .notDetermined else { return }
    let status = await ATTrackingManager.requestTrackingAuthorization()
    switch status {
    case .authorized: /* use ASIdentifierManager.shared().advertisingIdentifier */ break
    default: /* no tracking */ break
    }
}

The prompt is non-customizable. The string above the prompt comes from NSUserTrackingUsageDescription. Do not ask in a pre-prompt that tries to coerce; Apple has rejected apps for doing so. Honor the user’s choice.

ATT consent rates hover around 20-30 % — design your business model assuming most users decline.

PrivacyInfo.xcprivacy (Xcode 15+)

The Privacy Manifest is a .xcprivacy file in your app and in every third-party SDK you bundle. As of May 2024, App Store Review requires it for any app or SDK on Apple’s Commonly Used Reason APIs list.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>NSPrivacyTracking</key>
    <false/>
    <key>NSPrivacyTrackingDomains</key>
    <array/>
    <key>NSPrivacyCollectedDataTypes</key>
    <array>
        <dict>
            <key>NSPrivacyCollectedDataType</key>
            <string>NSPrivacyCollectedDataTypeEmailAddress</string>
            <key>NSPrivacyCollectedDataTypeLinked</key>
            <true/>
            <key>NSPrivacyCollectedDataTypeTracking</key>
            <false/>
            <key>NSPrivacyCollectedDataTypePurposes</key>
            <array>
                <string>NSPrivacyCollectedDataTypePurposeAppFunctionality</string>
            </array>
        </dict>
    </array>
    <key>NSPrivacyAccessedAPITypes</key>
    <array>
        <dict>
            <key>NSPrivacyAccessedAPIType</key>
            <string>NSPrivacyAccessedAPICategoryUserDefaults</string>
            <key>NSPrivacyAccessedAPITypeReasons</key>
            <array>
                <string>CA92.1</string>  <!-- Access info for current user only -->
            </array>
        </dict>
    </array>
</dict>
</plist>

Required Reason API categories (current list, may grow):

  • File timestamp APIs (creationDate, modificationDate)
  • System boot time
  • Disk space
  • Active keyboard
  • UserDefaults

Each requires a numeric reason code documented in Apple’s list. Pick the most specific reason that matches your use; don’t pick a permissive one to “cover yourself.”

For tracking, list every domain involved in cross-app/cross-site tracking under NSPrivacyTrackingDomains. iOS 17+ silently blocks DNS resolution for unlisted tracking domains when ATT consent is denied. Missing entries = silent broken functionality.

Privacy Nutrition Labels

In App Store Connect, declare what data your app collects and how it’s used. Categories:

  • Identifiers — user ID, device ID
  • Usage Data — interactions, ad clicks
  • Diagnostics — crash data, performance data
  • Location — coarse, precise
  • Contact Info — name, email, phone, address
  • User Content — messages, photos
  • Financial Info — payment info, credit info
  • Health & Fitness
  • Sensitive Info
  • Browsing History
  • Search History
  • Purchases
  • Contacts
  • Other Data

For each: is it linked to user identity? Used for tracking? What’s the purpose (analytics, product personalization, app functionality, third-party advertising)?

The labels appear in the App Store listing. Inaccurate labels are a violation of the Developer Program License Agreement and can trigger app removal. Audit yearly and after every SDK addition.

App Privacy Report (iOS 15.2+)

Users can enable “Record App Activity” in Settings → Privacy → App Privacy Report. The OS logs:

  • Permissions accessed (camera, mic, location, contacts, etc.) with timestamp
  • Network domains contacted
  • Domains contacted by app in the last 7 days

Users see your app’s data access in plain language. If your nutrition label says “we don’t collect location” but the privacy report shows daily location pings, users notice — and complain publicly. The report is a forcing function for honesty.

Data minimization principles

The lasting protection is collecting less data in the first place:

  1. Don’t ask if you don’t need. Every permission prompt has friction; every unused permission is liability.
  2. Coarse over precise. Location: ask for “while using” not “always”; precision: reduced (city-level) not full unless you need it.
  3. Local over server. Process on-device when possible (Core ML, on-device speech, local search) instead of sending data up.
  4. Ephemeral over persistent. Keep what you need for the session; don’t archive what you don’t need to recall.
  5. Hash or tokenize identifiers. If you only need to know “same user across requests,” send a hash of the user ID, not the ID itself.
  6. Delete on demand. Implement actual data deletion (not just account deactivation) within 30 days of request — GDPR and CCPA require it; the App Store requires an in-app delete-account flow.

GDPR & CCPA touchpoints

Compliance is mostly back-end policy, but client-side concerns:

  • Consent flow for non-essential data collection in the EU/UK
  • Privacy policy linked from Settings and the App Store listing
  • Data export — give users a download of their data (often a server endpoint surfaced in-app)
  • Account deletion — Apple requires this in-app since iOS 16 / June 2022
  • Children’s data (COPPA, GDPR-K) — different rules under 13/16; if applicable, age-gate

A senior engineer doesn’t need to write the privacy policy but must know what features the lawyers’ requirements translate to in code.

In the wild

Apple’s first-party apps (Notes, Maps, Photos) demonstrate the gold standard — minimal data collection, all on-device processing where possible, clear and specific usage strings. DuckDuckGo and Firefox Focus market themselves on the same principles, with extensive NSPrivacyTrackingDomains blocklists and zero linked-identifier data types in their nutrition labels.

Common misconceptions

  1. “Privacy is just legal stuff.” It’s also a hard App Store Review gate and a public reputation issue. Engineers own the implementation.
  2. “We don’t need a Privacy Manifest because we’re a simple app.” If you use UserDefaults or file timestamps, you need one. That’s almost every app.
  3. “Vague usage strings are safer because they don’t commit to anything.” Apple Review rejects vague strings. Be specific or be rejected.
  4. “ATT prompt is optional.” Required if you use IDFA or share any tracking identifier. Missing prompt + tracking = removal.
  5. “Account deletion via email is fine.” Apple requires in-app deletion since iOS 16. Email-only is a rejection.

Seasoned engineer’s take

Privacy is increasingly a technical discipline, not just a policy one. The Privacy Manifest, ATT prompts, App Privacy Report, and Required Reason APIs are all implemented in code. Treat them as architecture concerns from day one of a new project — retrofitting compliance into a shipped app costs weeks and risks app removal. The good news: every privacy-respecting design choice (data minimization, on-device processing, ephemeral storage) also reduces breach blast radius and infrastructure cost. Privacy and engineering quality converge.

TIP: Add PrivacyInfo.xcprivacy to your starter template before writing any feature code. Each new dependency or API addition triggers a manifest review. Cheap when habitual; expensive as a pre-submission scramble.

WARNING: Don’t lie on nutrition labels to look better. The App Privacy Report makes lies user-visible; the discrepancy is a guaranteed PR story.

Interview corner

Junior: “What’s NSCameraUsageDescription?” The Info.plist string shown to the user when your app first requests camera access. Required to use the camera; must be specific and user-meaningful or App Store Review rejects.

Mid: “What’s the Privacy Manifest and what does it contain?” A PrivacyInfo.xcprivacy file required for apps and SDKs since Xcode 15. Declares tracked domains, collected data types and purposes, and required-reason API usage (UserDefaults, file timestamps, etc.). Missing or inaccurate manifests cause App Store Review rejections.

Senior: “Design a privacy strategy for a new health-tracking app.” Start from data minimization: process everything on-device with Core ML and the Health framework, sync only aggregates upward (and only if the user opts in). All permission strings written by the product team with concrete user-facing reasons. Manifest declares the minimum data types (probably Health & Fitness and Diagnostics) with Linked = false where possible. No ATT prompt because we don’t track. No third-party analytics SDKs in the initial release — own the metrics via first-party logging with strict PII filtering. Privacy policy reviewed by counsel and surfaced in Settings + onboarding. In-app account deletion flow that actually deletes server data within 30 days (GDPR/CCPA). Quarterly review walks the App Privacy Report on internal devices to verify our claims match reality. The architecture saves on backend costs, simplifies HIPAA conversations if we ever go US-clinical, and avoids the entire class of “you said you didn’t collect X but you do” disasters.

Red-flag answer: “Legal will handle that.” Reveals offloading of engineering responsibility.

Lab preview

Lab 9.1 includes adding a complete PrivacyInfo.xcprivacy to the starter notes app, with accurate data type declarations and Required Reason API entries.


Next: 9.10 — Security Auditing & Pentest for iOS