Core Location 授权新范式:CLServiceSession 声明式授权
What's new in location authorization
2024年6月10日
一句话判断
CLServiceSession 把 Core Location 授权从”你在什么时机调用什么方法”变成了”声明你需要什么等级的定位”,系统自动处理弹窗时机和状态恢复——复杂场景下代码量能减少 80%。
这场 Session 讲了什么
Core Location 的授权一直是个复杂的话题。用户可能授予了”使用期间”但只是”近似位置”,你的某个功能突然需要”精确位置”,就得调用 requestTemporaryFullAccuracy。如果用户之前选了”允许一次”然后切到别的 App,授权就丢了,你又得重新请求。不同授权状态的排列组合让代码变得很脆弱。
iOS 18 引入的 CLServiceSession 是一个声明式的授权模型。你创建一个 session 并告诉系统你需要什么(比如 .whenInUse),系统负责在合适的时机向用户弹出请求。如果你还需要精确位置,就再创建一个带 fullAccuracyPurposeKey 的 session——注意是”叠加”而不是”替换”。
Session 还介绍了隐式 Service Session 的概念:当你迭代 CLLocationUpdate.liveUpdates 或 CLMonitor.events 这些 AsyncSequence 时,系统自动认为你有一个 .whenInUse 的隐式 session。这意味着很多场景下你只需要删除旧的手动授权代码就行。
Diagnostic Properties 是另一个新特性,帮你理解当前的授权状态和 API 运行状况。
值得深挖的点
声明式 vs 过程式授权的本质区别
旧模式是过程式的:你检查当前状态,决定调用 requestWhenInUseAuthorization 还是 requestTemporaryFullAccuracy,还要处理 App 被 backgrounded 导致请求中断的情况。每个分支都要写测试,但授权相关的测试本身就很难写。
新模式是声明式的:你创建一个 CLServiceSession(.whenInUse),告诉系统”我这个功能需要使用期间的位置权限”。系统知道你的目标,会在合适的时机(App 在前台、用户正在用相关功能时)弹出授权请求。如果 App 被 backgrounded 了,系统会在下次合适的机会继续追求这个目标。
“叠加”(layering)是这个设计的关键词。你的 App 通常只需要 .whenInUse,就持有一个基础 session。当用户开启导航功能时,你叠加一个需要精确位置的 session。两个 session 同时存在,系统取它们的并集。功能结束后释放精确位置 session,基础 session 仍然在。这种设计让每个功能模块独立管理自己的授权需求,不需要跨模块协调。
隐式 Session:删代码就是迁移
CLLocationUpdate.liveUpdates 的迭代行为现在隐含了一个 .whenInUse 的 service session。如果你的 App 原来在迭代 liveUpdates 之前手动调用 requestWhenInUseAuthorization,现在直接删掉那段代码就行。
但有些场景你不希望迭代自动触发授权弹窗(比如 App 初始化时你可能只想静默检查位置)。这时可以通过新的 API 禁用隐式 session 行为。大多数 App 不需要这个,但对于授权流程有精细控制需求的大型项目来说是个重要的安全阀。
代码片段
基础 Service Session
import CoreLocation
// 声明式授权:告诉系统你需要"使用期间"的位置权限
let basicSession = CLServiceSession(.whenInUse)
// 如果某个功能需要精确位置,叠加一个 session
let navigationSession = CLServiceSession(
.whenInUse,
fullAccuracyPurposeKey: "Navigation" // 对应 Info.plist 中的描述
)
// 功能结束后释放精确位置 session
// navigationSession 超出作用域后自动释放
// basicSession 仍然保持
场景:地图 App 正常浏览只需要近似位置,开启导航时叠加精确位置需求。
坑点:fullAccuracyPurposeKey 必须在 Info.plist 的 NSLocationTemporaryFullAccuracyUsageDescription 字典中有对应的描述,否则系统无法向用户解释为什么需要精确位置。
利用隐式 Session 简化代码
// 之前的写法:手动处理授权
class LocationManager: NSObject, CLLocationManagerDelegate {
let manager = CLLocationManager()
func startTracking() {
// 检查授权状态,未决定则请求
if manager.authorizationStatus == .notDetermined {
manager.requestWhenInUseAuthorization()
}
// 还要处理 delegate 回调中的状态变化...
}
}
// iOS 18 新写法:直接迭代,隐式持有 session
func trackLocation() async {
// 迭代 liveUpdates 隐式创建 .whenInUse session
for await update in CLLocationUpdate.liveUpdates {
guard let location = update.location else { return }
processLocation(location)
}
}
场景:只需要使用期间位置更新的简单场景。直接迭代 AsyncSequence,授权由系统自动处理。
Diagnostic Properties 检查授权状态
// 新的诊断属性帮助你理解当前状态
let manager = CLLocationManager()
// 检查当前授权的精确度
if manager.accuracyAuthorization == .fullAccuracy {
// 拥有精确位置权限
} else {
// 只有近似位置,如果需要精确位置就创建带 purposeKey 的 session
let preciseSession = CLServiceSession(
.whenInUse,
fullAccuracyPurposeKey: "TurnByTurn"
)
}
场景:在创建 session 之前检查当前状态,决定是否需要叠加精确位置需求。
最佳实践
- 将
CLServiceSession理解为”目标声明”而非”动作指令”,不要试图控制弹窗时机 - 用叠加(layering)的方式管理不同功能模块的授权需求,每个模块独立创建和释放 session
- 优先利用隐式 session(迭代 liveUpdates/events 时),只删除旧的手动授权代码
fullAccuracyPurposeKey要写得具体,让用户理解为什么这个功能需要精确位置- CLServiceSession 是 Sendable 的不可变对象,可以安全地在并发环境中使用
还有什么值得关注
- Diagnostic Properties 还包括 Core Location 其他 API 对象的运行状态诊断
- 如果不想让迭代自动触发授权请求,可以通过新 API 禁用隐式 session 行为 -去年的 CLLocationUpdate 和 CLMonitor 是这次 session 的基础,建议先了解这两个 API