隐私功能更新
What's new in privacy
Invalid Date
一句话判断
Apple 把隐私围墙又砌高了一层——从设备命名、位置权限、剪贴板访问到登录项管理,每一刀都切在了开发者的”数据收集习惯”上。
这场 Session 讲了什么
这场 Session 盘点了 macOS Ventura、iOS 16 和 iPadOS 16 中所有与隐私相关的新变化。涉及面很广,从开发者的角度可以归纳成几个方向:
设备标识和位置隐私。 iOS 16 引入了 com.apple.developer.device-name entitlement,App 可以通过这个权限获取用户给设备起的名字(比如”张三的 iPhone”),用于 AirDrop 之类的场景。但别高兴太早,这个 entitlement 需要审核批准,不是随便就能用的。位置权限方面,Control Center 里新增了位置访问指示器,用户能清楚看到哪个 App 在什么时间访问了位置。如果你的 App 在后台频繁访问位置,用户现在有了更直观的”证据”。
安全基础设施的升级。 macOS Ventura 对 Gatekeeper 做了强化——从互联网下载的 App 首次运行时,除了签名和公证检查之外,还会检查 App 是否符合安全策略(NSUpdateSecurityPolicy)。开发者现在可以在 Info.plist 里声明允许加载的动态库路径,减少被注入攻击的风险。
登录项和后台服务透明化。 这是一个大改动。macOS 13 用新的 SMAppService API 替代了传统的 Login Items 管理方式。所有注册了登录项或后台服务的 App 都会出现在系统设置的”登录项”面板里,用户可以一目了然地看到谁在后台运行,并且可以一键关闭。之前很多 App 通过 LaunchAgents 或者 LaunchDaemons 偷偷常驻后台的好日子到头了。
剪贴板访问限制。 从 iOS 16 开始,App 访问剪贴板需要用户明确授权。系统会弹出一个权限对话框,让用户选择”允许粘贴”、“拒绝”或”总是允许”。对于频繁需要粘贴操作的 App(比如密码管理器),UIKit 提供了新的 UIPasteControl,这是一个标准化的粘贴按钮,用户点击后不需要额外弹窗就能完成粘贴。
媒体设备发现需要权限。 App 想要发现局域网中的 AirPlay 设备或其他媒体设备,现在需要用户授权。这个变化对音视频类 App 影响比较大。
Private Access Tokens。 Apple 和 Cloudflare 合作推出的新机制,让设备可以在不暴露用户身份的情况下证明自己是真实设备。对于 App 内的网络请求,这可以替代 CAPTCHA,用户不再需要手动完成”选择所有包含红绿灯的图片”这种验证了。
Passkeys。 基于FIDO2 标准的无密码登录方案,用设备生物识别(Face ID/Touch ID)替代传统密码。iCloud Keychain 会在设备间同步 Passkeys,跨平台使用时通过 QR 码配对。
Safety Check。 这是面向用户的隐私功能,但在技术实现上值得关注。它允许用户一键重置所有 App 的隐私权限、退出 iCloud、紧急切断与他人的位置共享。如果你的 App 依赖持续的位置共享或 HomeKit 访问,需要考虑 Safety Check 对用户体验的影响。
值得深挖的点
SMAppService:后台常驻的透明化革命。 之前 macOS 的 Login Items 管理非常混乱——LaunchAgents、LaunchDaemons、Startup Items 各管各的,用户根本不知道谁在后台跑。SMAppService 把这一切统一了:App 通过标准 API 注册登录项,系统统一展示和管理。对于开发者来说,迁移成本很低,但好处是用户不会再因为”不知道谁在后台跑”而直接卸载你的 App。
剪贴板权限的精细化控制。 之前 UIPasteboard.general.string 一行代码就能读到剪贴板内容,现在要么等弹窗授权,要么用 UIPasteControl。UIPasteControl 的设计思路很有意思——它不是给开发者一个 API 去读剪贴板,而是提供一个系统级的 UI 组件,让用户主动”推送”内容到 App。这个设计把数据流向的控制权交给了用户,比弹窗授权更优雅。
代码片段
使用 SMAppService 注册登录项:
import ServiceManagement
// 注册为登录项
let service = SMAppService.loginItem(identifier: "com.example.myapp")
do {
try service.register()
} catch {
print("注册登录项失败: \(error)")
}
// 检查当前状态
let status = service.status
// .notRegistered, .enabled, .requiresApproval, .notFound
// 取消注册
try service.unregister()
注意 status 属性可以返回 requiresApproval,说明用户还没在系统设置里批准。你的 App 应该引导用户去系统设置完成授权。
配置安全策略(NSUpdateSecurityPolicy):
<key>NSUpdateSecurityPolicy</key>
<dict>
<key>AllowPlugins</key>
<true/>
<key>AllowedLoaders</key>
<array>
<string>com.example.loader</string>
</array>
</dict>
这个配置告诉系统你的 App 允许加载哪些外部代码。AllowPlugins 允许加载插件,AllowedLoaders 指定了允许加载你 App 的其他进程标识符。
最佳实践
如果你的 App 需要读取剪贴板,优先使用 UIPasteControl 而不是直接访问 UIPasteboard。粘贴按钮的样式可以自定义(颜色、圆角、图标),但交互流程是系统控制的。这样做的好处是用户不会看到”xxx 想要粘贴”的弹窗,体验更流畅。
对于需要后台运行的服务,尽早迁移到 SMAppService。旧的 LaunchAgent plist 方式虽然在 macOS 13 上还能用,但不会出现在系统设置的”登录项”面板里,用户可能会觉得你的 App 在偷偷摸摸干事情。
如果你的 App 涉及网络请求且经常被 Cloudflare 之类的服务拦下来做 CAPTCHA 验证,研究一下 Private Access Tokens。它可以证明请求来自合法的 Apple 设备,省掉 CAPTCHA 环节。接入方式是在 HTTP 请求头里附带 PrivateToken challenge-response。
还有什么值得关注
- 设备名称 entitlement 需要在 App Store Connect 里申请,不是所有 App 都能拿到。适合 AirDrop、设备管理、多设备同步等场景。
- iOS 16 中,
NSLocationWhenInUseUsageDescription的弹窗文案下面会显示你的 App 最近一次访问位置的时间,如果你的 App 在前台时偷偷调了位置 API,用户能看得清清楚楚。 - Safety Check 对 HomeKit 和 Find My 的影响需要特别关注。如果你的 App 依赖这些权限提供核心功能,需要在 App 内引导用户重新授权。
- Passkeys 的跨平台认证流程使用 QR 码 + 蓝牙proximity check,整个流程的安全假设是”两台设备在物理距离很近的地方”。