iOS 权限系统详解:从 Info.plist 到弹窗交互
iOS 权限以用户隐私为核心:Info.plist 写清用途描述、系统弹窗询问、设置里可随时关闭,跨端开发者必须按需申请、优雅降级,否则审核被拒或功能静默失效。
一句话概括
iOS 权限体系的灵魂就一句话:用到敏感能力前,必须在 Info.plist 写清「为什么要用」,系统才弹窗问用户,且用户拒绝后你没法再弹第二次。和 Android「请求即授权、还能每次再问」完全不同,iOS 把「知情权」和「随时撤销权」牢牢交还给用户。
对跨端开发者,最坑的不是代码怎么写,而是:权限描述漏配 → 调用 API 时系统静默失败、连弹窗都没有;或者启动就一次性申请所有权限 → 用户一上来就全拒、审核还可能被毙。理解这套机制,才能在接入摄像头、定位、推送时一次配对。
核心知识点
1. Info.plist 的 UsageDescription:权限的「入场券」
每个敏感能力都有对应的 NSxxxUsageDescription 键,写在 Info.plist 里。系统只在这个键存在时才会弹窗;没有它,调用 API 直接失败且无任何提示。最常用的几个:
| 能力 | Info.plist Key | 典型场景 |
|---|---|---|
| 摄像头 | NSCameraUsageDescription | 扫码、拍照 |
| 麦克风 | NSMicrophoneUsageDescription | 录音、视频通话 |
| 相册(读写) | NSPhotoLibraryUsageDescription | 选照片 |
| 相册(仅写) | NSPhotoLibraryAddUsageDescription | 存图不读 |
| 使用期间定位 | NSLocationWhenInUseUsageDescription | 前台导航 |
| 始终定位 | NSLocationAlwaysAndWhenInUseUsageDescription | 后台轨迹(iOS 11+) |
| 通知 | 无 plist 键,走 UNUserNotificationCenter | 推送 |
| 跟踪(IDFA) | NSUserTrackingUsageDescription | 广告归因(ATT) |
关键规则:哪怕你自己的代码没调系统 API,只要集成的第三方 SDK 内部调了,也得在 Info.plist 配对应描述——否则 SDK 触发请求时系统因缺描述而静默失败。这是跨端项目最高频的「功能莫名其妙不工作」元凶。
1
2
// ❌ 只配了 NSCameraUsageDescription,但 image_picker 内部还读相册 → 选图静默失败
// ✅ 摄像头、相册读、麦克风按实际用到的能力一次性配齐
2. 弹窗触发时机:首次调用 API 才弹,拒了就再也弹不了
iOS 和 Android 最大的不同在这里:
1
2
3
4
第一次调用 camera API → 系统弹 NSCameraUsageDescription 弹窗
用户选择 → 结果存进系统权限库
第二次调用 → 不再弹窗,直接返回之前的结果
用户点了「不允许」 → 之后所有请求直接返回 denied,绝不再弹
也就是说:用户拒绝后,你的 App 没有任何 API 能再次弹窗。唯一出路是引导用户去「系统设置 → 你的 App」手动开。所以描述文字要写清楚真实用途,别糊弄。
3. 定位权限:When In Use vs Always + iOS 14 精确位置
定位是权限里最绕的一块。层次是 不允许 → When In Use(前台)→ Always(前后台),后者需要先在 Signing & Capabilities 开 Background Modes 的 Location updates:
1
2
3
4
5
6
7
8
let manager = CLLocationManager()
manager.delegate = self
manager.desiredAccuracy = kCLLocationAccuracyBest
// 先要前台定位
manager.requestWhenInUseAuthorization()
// 之后真需要后台时再要(不是一上来就 Always)
manager.requestAlwaysAuthorization()
iOS 14 起多了一个精确位置开关:即使用户授权了定位,也可能只给你 1–2 公里模糊坐标。需要精确位置时要用 requestTemporaryFullAccuracyAuthorization(withPurposeKey:) 临时申请,并在 Info.plist 配 NSLocationTemporaryUsageDescriptionDictionary。真机定位「偏差巨大」先别怀疑代码,去设置里看「精确位置」开关是不是被关了。
4. 通知权限:授权与注册是两步
通知走 UserNotifications 框架,注意请求授权和向 APNs 注册 token 是两件事,顺序不能反:
1
2
3
4
5
6
7
8
9
10
import UserNotifications
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .badge]) { granted, _ in
if granted {
// 用户同意后才注册远程推送,拿到 deviceToken
DispatchQueue.main.async {
UIApplication.shared.registerForRemoteNotifications()
}
}
}
收不到 token 的常见原因:Entitlements 没开 Push Notifications、没配后台模式、或网络连不上 APNs。注意:didRegisterForRemoteNotificationsWithDeviceToken 的回调和「用户是否授权通知显示(横幅/声音/角标)」无关——只要 App 开启了 Push 能力且设备连得上 APNs,哪怕用户拒绝授权也会返回 token(静默推送照样能送达);用户授权只决定能不能弹横幅,不影响 token 获取。
5. iOS 14+ 新权限 与 iOS 17 隐私清单(重点更新)
- 本地网络
NSLocalNetworkUsageDescription:访问局域网设备(BLE / mDNS / 打印)时弹窗,常配NSBonjourServices - 粘贴板:iOS 14 读取会顶部横幅提示,iOS 16+ 读前需授权,别在启动时偷读
- ATT
NSUserTrackingUsageDescription:要 IDFA 必须弹,且要在applicationDidBecomeActive之后弹,否则上架会被拒 - 隐私清单
PrivacyInfo.xcprivacy(iOS 17 起强制):自 2024-05-01 起,提审 App(及列出的第三方 SDK 如 Firebase、SDWebImage 等)必须带隐私清单,声明「收集了哪些数据」「用了哪些需说明理由的 API(如 UserDefaults、文件时间戳)」。漏了会收到ITMS-91053警告甚至被拒——这是原文没有、但如今面试和实际上架都绕不开的新知识点。
其实你每天都在用
image_picker选照片黑屏/没反应:九成是 Info.plist 漏了NSPhotoLibraryUsageDescription,系统静默失败- 后台导航没轨迹:没在 Signing & Capabilities 开 Location updates 后台模式,也没申请 Always 授权
- 推送收不到 token:Entitlements 没开 Push Notifications,或没先
requestAuthorization就registerForRemoteNotifications - 广告 SDK 要 IDFA 被拒:没写
NSUserTrackingUsageDescription,或 ATT 弹窗时机不对 - 真机定位偏了几公里:iOS 14+ 的「精确位置」开关被用户关了,不是你代码的问题
permission_handler里isPermanentlyDenied:用户之前点过「不允许」,只能引导去系统设置开- 上架收到
ITMS-91053:缺隐私清单,第三方 SDK 用了需说明理由的 API 没声明 - 启动时弹一堆权限请求:用户一上来全拒,体验崩、审核也可能被毙——应该「用到再要」
常见误解(FAQ)
❌ 误区一:「用户拒绝权限后,我换个时机再调一次 API 就能重新弹窗」
不能。iOS 一旦用户选了「不允许」,后续所有请求直接返回 denied,系统绝不再弹。正确做法是检测 denied / permanentlyDenied,弹对话框引导用户去「系统设置 → 你的 App」手动开启。
❌ 误区二:「只有我自己代码调了系统 API,才需要在 Info.plist 配描述」
错。第三方 SDK 或原生插件内部调用了摄像头 / 定位 / 相册,同样触发权限请求,而系统只看 Info.plist 有没有对应 UsageDescription。漏配的结果不是崩溃,而是静默失败——功能没反应、还没报错,最难查。
❌ 误区三:「App 启动时就一次性把相机、定位、通讯录全申请了,省事」
这是审核被拒和用户体验翻车的高频原因。Apple 建议按需、逐个申请,且描述要具体真实。一次性全要,用户往往直接全拒,你反而啥都拿不到。
❌ 误区四:「NSLocationAlwaysUsageDescription 还在用,配它就够了」
已废弃。iOS 11+ 用 NSLocationAlwaysAndWhenInUseUsageDescription,前者只在前台场景会被忽略。而且 Always 权限审核更严,不是真有后台定位需求别申请。
❌ 误区五:「权限描述随便写两句就能过审」
审核会看描述是否「真实、具体、和你的功能匹配」。写「用于优化广告体验」却没有个性化广告功能、写「拍照」但 App 里压根没拍照,都会被拒。好描述是「扫描商品条码以快速加入购物车」这种具体到动作的句子。
一句话总结
iOS 权限的本质是把知情权和撤销权交给用户:Info.plist 写清用途是入场券、系统弹窗只问一次、拒绝后只能引导去设置——所以「按需申请、描述写真、被拒优雅降级」不是政治正确,而是你的 App 能不能过审、用户愿不愿意给权限的硬道理,而 iOS 17 起的隐私清单则把这个逻辑从「代码层」又往前推到了「提审层」。