WWDC Quick Look 💓 By SwiftGGTeam
Discover streamlined location updates

Discover streamlined location updates

观看原视频

Highlight

Core Location 在 iOS 17 引入了 CLLocationUpdate 类,用一行 for try await 代码替代了传统的 delegate 模式,原生支持 Swift Concurrency,并内置自动暂停/恢复机制来节省电量。

核心内容

以前获取位置更新要写一堆代码:创建 CLLocationManager、设置 delegate、实现 didUpdateLocations、手动调用 startUpdatingLocation()stopUpdatingLocation()。代码分散在多个方法里,状态管理复杂,还要自己处理后台保活和电量优化。

Apple 今年推出了 CLLocationUpdate,把这一切压缩成了一行代码。它返回一个 AsyncSequence,直接用 for try await 遍历就能拿到位置更新。不需要 delegate,不需要手动启停,代码从分散的回调变成了线性的顺序逻辑。

更关键的是自动暂停/恢复。当设备静止一段时间后,系统会自动暂停更新,发送一个 isStationary == true 的标记。设备再次移动时自动恢复。这省去了开发者自己判断静止状态的麻烦,也减少了后台唤醒次数。

后台场景也做了简化。如果你的 app 有 Live Activity,后台位置更新自动生效。如果没有,创建一个 CLBackgroundActivitySession 就能保持后台能力,系统会显示蓝色位置指示条让用户知情。

详细内容

CLLocationUpdate 的基本用法

00:27)获取位置更新现在只需要一行代码:

import CoreLocation

for try await update in CLLocationUpdate.liveUpdates() {
    print("My current location: \(update.location)")
}

关键点:

  • CLLocationUpdate.liveUpdates() 返回 AsyncSequence,支持 for try await
  • 每个 update 包含 location: CLLocation?isStationary: Bool
  • locationnil 表示当前无可用位置,区别于停止更新
  • isStationarytrue 时设备已静止,可以 break 跳出循环停止更新

使用预设配置

04:41liveUpdates() 接受一个可选的 LiveConfiguration 参数:

for try await update in CLLocationUpdate.liveUpdates(.fitness) {
    print("Location: \(update.location)")
}

LiveConfiguration 包含以下预设:

  • .default — 默认配置
  • .automotiveNavigation — 车载导航
  • .otherNavigation — 其他导航
  • .fitness — 运动健身
  • .airborne — 航空

这些对应旧的 CLActivityType,迁移时选择对应配置即可获得相同的位置精度体验。

AsyncSequence 的过滤能力

03:17)返回的 UpdatesAsyncSequence,可以直接使用过滤操作:

let fastUpdate = try await CLLocationUpdate.liveUpdates()
    .first { update in
        guard let speed = update.location?.speed else { return false }
        return speed > 200  // 200 米/秒,约 447 英里/小时
    }

关键点:

  • first 等过滤操作会阻塞直到匹配条件
  • 匹配成功后 AsyncSequence 自动结束,无需手动停止
  • 基于 horizontalAccuracy 的过滤要小心,可能长时间无法匹配

后台位置更新

06:04)最佳方式是配合 Live Activity。只要 Live Activity 处于活跃状态,app 就能在后台接收位置更新,无需额外配置。

如果没有 Live Activity,使用 CLBackgroundActivitySession

class LocationManager {
    var backgroundActivity: CLBackgroundActivitySession?

    func startBackgroundUpdates() {
        // 必须赋值给属性,不能用局部变量
        backgroundActivity = CLBackgroundActivitySession()

        Task {
            for try await update in CLLocationUpdate.liveUpdates() {
                print("Location: \(update.location)")
                if update.isStationary {
                    break
                }
            }
        }
    }

    func stop() {
        backgroundActivity?.invalidate()
        backgroundActivity = nil
    }
}

关键点:

  • CLBackgroundActivitySession 必须持有引用,dealloc 会自动 invalidate
  • app 的 UIBackgroundModes 仍需包含 location
  • 新的 session 只能从前台启动,后台只能 rejoin 已有的 session

自动暂停与恢复

09:19)设备静止时,系统会自动暂停更新:

  1. 设备静止足够长时间 → 触发自动暂停
  2. 发送 isStationary = true 的更新,location 非 nil
  3. 设备再次移动 → 自动恢复
  4. 发送 isStationary = false 的更新,继续递送位置

这让 app 无需自己判断静止状态,也避免了处理冗余位置数据。

App 生命周期与恢复

10:39)后台运行期间,app 可能在以下状态间切换:

  • 前台运行 → 后台运行:正常过渡
  • 后台运行 → 挂起:无更新可递送时自动发生
  • 挂起 → 后台运行:更新可用时自动恢复,无需操作
  • 后台运行/挂起 → 终止:崩溃、用户关闭或系统回收资源

app 被终止后,系统在位置更新可用时会自动后台启动 app。此时需要在 application(_:didFinishLaunchingWithOptions:) 中重建 liveUpdates()CLBackgroundActivitySession

func application(_ application: UIApplication,
                 didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    // 重建后台 session(这是 rejoin,不是新建)
    backgroundActivity = CLBackgroundActivitySession()

    Task {
        for try await update in CLLocationUpdate.liveUpdates() {
            handleLocationUpdate(update)
        }
    }
    return true
}

关键点:

  • 重建操作必须在收到后台启动后立即执行
  • 后台重建的 CLBackgroundActivitySession 是 rejoin 已有 session,符合后台启动限制

核心启发

1. 运动追踪 App 的电量优化

  • 做什么:用 CLLocationUpdate 替换旧的 CLLocationManager delegate 模式
  • 为什么值得做:自动暂停/恢复机制让设备静止时停止耗电,无需自己写静止检测逻辑
  • 怎么开始:把 locationManager.startUpdatingLocation() 换成 for try await update in CLLocationUpdate.liveUpdates(.fitness)

2. 外卖/打车实时位置共享

  • 做什么:给骑手/司机端加上 Live Activity,配合 CLLocationUpdate 实时上报位置
  • 为什么值得做:Live Activity 让后台位置更新零配置生效,用户随时能在锁屏看到配送进度
  • 怎么开始:先实现 Live Activity(参考 ActivityKit),然后直接在前台启动 CLLocationUpdate.liveUpdates()

3. 地理围栏 + 位置追踪的组合场景

  • 做什么:用 CLMonitor 监控地理围栏事件,用 CLLocationUpdate 追踪进入围栏后的详细轨迹
  • 为什么值得做:两者结合可以精确知道”何时到达”和”在区域内做了什么”
  • 怎么开始:先看 “Meet Core Location Monitor” session 了解 CLMonitor,再叠加 CLLocationUpdate

4. 自动暂停的运动记录

  • 做什么:利用 isStationary 自动检测用户停下休息,暂停记录并提示
  • 为什么值得做:以前要自己维护速度阈值和计时器,现在系统直接给出静止状态
  • 怎么开始:在 for try await 循环里检查 update.isStationary,为 true 时暂停记录并显示”已暂停”

关联 Session

评论

GitHub Issues · utterances