What's new in location authorization
Maps & Location 进阶 20m

Core Location 授权新范式:CLServiceSession 声明式授权

What's new in location authorization

2024年6月10日

在 Apple 官方观看视频

一句话判断

CLServiceSession 把 Core Location 授权从”你在什么时机调用什么方法”变成了”声明你需要什么等级的定位”,系统自动处理弹窗时机和状态恢复——复杂场景下代码量能减少 80%。

这场 Session 讲了什么

Core Location 的授权一直是个复杂的话题。用户可能授予了”使用期间”但只是”近似位置”,你的某个功能突然需要”精确位置”,就得调用 requestTemporaryFullAccuracy。如果用户之前选了”允许一次”然后切到别的 App,授权就丢了,你又得重新请求。不同授权状态的排列组合让代码变得很脆弱。

iOS 18 引入的 CLServiceSession 是一个声明式的授权模型。你创建一个 session 并告诉系统你需要什么(比如 .whenInUse),系统负责在合适的时机向用户弹出请求。如果你还需要精确位置,就再创建一个带 fullAccuracyPurposeKey 的 session——注意是”叠加”而不是”替换”。

Session 还介绍了隐式 Service Session 的概念:当你迭代 CLLocationUpdate.liveUpdatesCLMonitor.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
WWDC 2024