Sensitive features — camera, location, notifications — require the user’s permission at runtime and a usage description in your Info.plist. Capabilities unlock system services like push and iCloud.
Access to private data and hardware is gated: you declare a usage-description string in Info.plist (shown in the system prompt) and request permission at runtime, and the user can grant, deny, or later revoke it. Your app must handle every outcome gracefully — assuming access, or forgetting the Info.plist string, causes the classic crash on first use.
Info.plist string required required — Requesting a sensitive permission without its usage-description key in Info.plist crashes the app immediately. Add the matching NS…UsageDescription string for every permission you request.import CoreLocation
class LocationManager: NSObject, CLLocationManagerDelegate {
private let manager = CLLocationManager()
func request() {
manager.delegate = self
manager.requestWhenInUseAuthorization() // shows the system prompt
}
// handle the user's decision — grant, deny, or "not yet"
func locationManagerDidChangeAuthorization(_ m: CLLocationManager) {
switch m.authorizationStatus {
case .authorizedWhenInUse: m.startUpdatingLocation()
case .denied, .restricted: print("show a graceful fallback")
default: break
}
}
}
// requires NSLocationWhenInUseUsageDescription in Info.plist, or it crashes.Timing and context matter: request a permission at the moment the user does something that needs it, with a brief in-app explanation first, rather than blasting every prompt at launch. A permission asked in context with a clear reason is far more likely to be granted — and the user experience follows Apple’s guidelines, which reviewers check.
Good permission UX:
- ask IN CONTEXT: request camera access when the user taps "Scan", not at launch
- PRE-PROMPT: show a short in-app explanation before the system dialog
(the system prompt appears only once — a denial is costly)
- write a clear NS...UsageDescription; the user reads it in the dialog
- handle DENIAL: offer a fallback + a "go to Settings" path (you can't re-prompt)
Blasting every prompt at launch => denials + a worse review. Ask when it
matters, and explain why.Some system services — push notifications, iCloud, Sign in with Apple, background modes, HealthKit — are enabled as capabilities on your app target rather than requested at runtime. Toggling a capability updates your entitlements and provisioning. Knowing capabilities live in the target settings (not code) saves confusion when wiring up features like push.
// capabilities are enabled on the TARGET (Signing & Capabilities tab),
// not in code — they add entitlements + configure provisioning.
// e.g. Push Notifications capability -> then register in code:
import UserNotifications
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound]) { granted, _ in
if granted { DispatchQueue.main.async { UIApplication.shared.registerForRemoteNotifications() } }
}
// common capabilities: Push, iCloud, Sign in with Apple, Background Modes,
// HealthKit, App Groups. Toggle in Xcode -> entitlements update automatically.Apple enforces privacy strictly: you complete a privacy "nutrition label" describing what data you collect, and misusing permissions or missing disclosures gets an app rejected. Request only what you need, explain why, and honor the user’s choice. Treating privacy as a design constraint — not an afterthought — is both an ethical baseline and a requirement to ship.
App Store privacy expectations:
- privacy "nutrition label": declare what data you collect + how it's used
- request only the permissions you actually need (least privilege)
- App Tracking Transparency prompt required to track across apps
- don't fingerprint or collect data without disclosure -> rejection
- honor the user's choice; degrade gracefully when denied
Privacy is a REVIEW GATE, not optional polish. Design features around the
minimum data + clear disclosure from the start.