Privacy UX Patterns
Comprehensive guide to privacy-first app design. Apple Design Award Social Impact winners handle data ethically, and privacy-first design is a key differentiator.
Overview
Privacy manifests (PrivacyInfo.xcprivacy) are Apple's framework for transparency about data collection and tracking. Combined with App Tracking Transparency and just-in-time permission requests, they help users make informed choices about their data.
This skill covers creating privacy manifests, requesting system permissions with excellent UX, implementing App Tracking Transparency, managing tracking domains, using Required Reason APIs, and preparing accurate Privacy Nutrition Labels.
When to Use This Skill
- Creating privacy manifests (PrivacyInfo.xcprivacy)
- Requesting system permissions (Camera, Location, etc.)
- Implementing App Tracking Transparency (ATT)
- Preparing Privacy Nutrition Labels for App Store Connect
- Managing tracking domains to avoid accidental tracking
- Using Required Reason APIs (NSFileSystemFreeSize, UserDefaults, etc.)
- Explaining data usage to users transparently
- Debugging privacy-related App Store rejections
System Requirements
- iOS 14.5+ for App Tracking Transparency
- iOS 17+ for automatic tracking domain blocking
- Xcode 15+ for privacy reports and manifest editing
- Spring 2024+ for App Review enforcement
Part 1: Privacy Manifests (WWDC 2023/10060)
Creating a Privacy Manifest
Xcode Navigator:
- File β New β File
- Choose "App Privacy File"
- Name:
PrivacyInfo.xcprivacy
- Add to app target (or SDK framework)
File structure (Property 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>NSPrivacyCollectedDataTypes</key>
<array>
</array>
<key>NSPrivacyAccessedAPITypes</key>
<array>
</array>
</dict>
</plist>
NSPrivacyTracking Declaration
Does your app track users?
Tracking = combining user/device data from your app with data from other apps/websites to create a profile for targeted advertising or data broker purposes.
<key>NSPrivacyTracking</key>
<true/>
If true, you must also declare tracking domains:
<key>NSPrivacyTrackingDomains</key>
<array>
<string>tracking.example.com</string>
<string>analytics.example.com</string>
</array>
iOS 17 behavior: Network requests to tracking domains automatically blocked if user hasn't granted ATT permission.
NSPrivacyCollectedDataTypes
Declare all data your app collects:
<key>NSPrivacyCollectedDataTypes</key>
<array>
<dict>
<key>NSPrivacyCollectedDataType</key>
<string>NSPrivacyCollectedDataTypeName</string>
<key>NSPrivacyCollectedDataTypeLinked</key>
<true/>
<key>NSPrivacyCollectedDataTypeTracking</key>
<false/>
<key>NSPrivacyCollectedDataTypePurposes</key>
<array>
<string>NSPrivacyCollectedDataTypePurposeAppFunctionality</string>
<string>NSPrivacyCollectedDataTypePurposeAnalytics</string>
</array>
</dict>
</array>
Common data types:
NSPrivacyCollectedDataTypeName - User's name
NSPrivacyCollectedDataTypeEmailAddress
NSPrivacyCollectedDataTypePhoneNumber
NSPrivacyCollectedDataTypePhysicalAddress
NSPrivacyCollectedDataTypePreciseLocation
NSPrivacyCollectedDataTypeCoarseLocation
NSPrivacyCollectedDataTypePhotosorVideos
NSPrivacyCollectedDataTypeContacts
NSPrivacyCollectedDataTypeUserID
Common purposes:
NSPrivacyCollectedDataTypePurposeAppFunctionality
NSPrivacyCollectedDataTypePurposeAnalytics
NSPrivacyCollectedDataTypePurposeProductPersonalization
NSPrivacyCollectedDataTypePurposeDeveloperAdvertising
NSPrivacyCollectedDataTypePurposeThirdPartyAdvertising
NSPrivacyAccessedAPITypes
Declare Required Reason APIs (see Part 5):
<key>NSPrivacyAccessedAPITypes</key>
<array>
<dict>
<key>NSPrivacyAccessedAPIType</key>
<string>NSPrivacyAccessedAPICategoryFileTimestamp</string>
<key>NSPrivacyAccessedAPITypeReasons</key>
<array>
<string>C617.1</string>
</array>
</dict>
</array>
Part 2: Permission Request UX
Just-in-Time vs Up-Front
β Don't: Request all permissions at launch
func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
requestCameraPermission()
requestLocationPermission()
requestNotificationPermission()
requestPhotoLibraryPermission()
return true
}
β
Do: Request just-in-time when user triggers feature
@objc func takePhotoButtonTapped() {
showCameraEducation {
AVCaptureDevice.requestAccess(for: .video) { granted in
if granted {
self.openCamera()
} else {
self.showPermissionDeniedAlert()
}
}
}
}
Pre-Permission Education Screens
Explain why you need permission before showing system dialog:
func showCameraEducation(completion: @escaping () -> Void) {
let alert = UIAlertController(
title: "Take Photos",
message: "FoodSnap needs camera access to let you photograph your meals and get nutrition information.",
preferredStyle