PAYATHON 2026

watchOS WidgetKit 小组件如何请求位置并获取坐标?

数据小许

结论

NSWidgetWantsLocation = YES 是必要配置,但不能替代 App 的位置授权。包含小组件的 App 需要先请求位置权限。用户将使用位置的小组件添加到系统时,系统会询问是否把 App 的位置授权扩展给小组件。Apple 的 World Clock 提示说的就是这一步。

你列出的两个用途说明字符串都放在 Widget Extension 的 Info.plist 中,这可能是问题所在。Apple 的 WidgetKit 文档要求将相关位置用途说明放在包含小组件的 App 的 Info.plist 中;NSWidgetWantsLocation 则应放在 Widget Extension 的 Info.plist 中。Apple:Accessing location information in widgets

检查授权配置

  1. 在 Widget Extension 的 Info.plist 中保留:

    <key>NSWidgetWantsLocation</key>
    <true/>
  2. 在包含小组件的 App 的 Info.plist 中,配置与授权方式对应的用途说明。若 App 请求 When In Use 授权,至少应提供 NSLocationWhenInUseUsageDescription。只有确实需要 Always 授权时,才配置并请求相应的 Always 权限。

  3. 由 App 请求位置授权。用户已授权 Watch App,不一定代表也同意将授权扩展给小组件。对于 authorizedWhenInUse,用户同意扩展授权后,小组件才符合接收位置更新的条件。可以通过 CLLocationManager.isAuthorizedForWidgetUpdates 检查。Apple:isAuthorizedForWidgetUpdates

位置授权提示与用户添加小组件等系统流程有关。NSWidgetWantsLocation 不会立即弹出标准的 App 授权框。系统文档说明,用户添加使用位置的小组件时会被询问是否扩展授权;之后也可以在位置服务设置中更改选择。Apple:Accessing location information in widgets

在 timeline provider 中请求位置

WidgetKit 的 timeline provider 可以使用 CLLocationManager。保留 manager 和 delegate,在 provider 请求 timeline 时调用 requestLocation(),再于 delegate 回调中生成 timeline。不要假设启动定位后能同步拿到结果:Core Location 通过 delegate 异步返回位置,而且小组件不像 App 进程那样持续运行。Apple WWDC:Principles of great widgets

示意代码如下。实际项目中还需根据所用的 TimelineProvider 接口接入完成回调,并处理超时:

final class LocationReader: NSObject, CLLocationManagerDelegate {
    private let manager = CLLocationManager()
    var onResult: ((CLLocation?, Error?) -> Void)?

    override init() {
        super.init()
        manager.delegate = self
        manager.desiredAccuracy = kCLLocationAccuracyKilometer
    }

    func requestLocation() {
        guard manager.isAuthorizedForWidgetUpdates else {
            onResult?(nil, nil)
            return
        }

        manager.requestLocation()
    }

    func locationManager(
        _ manager: CLLocationManager,
        didUpdateLocations locations: [CLLocation]
    ) {
        onResult?(locations.last, nil)
    }

    func locationManager(
        _ manager: CLLocationManager,
        didFailWithError error: Error
    ) {
        onResult?(nil, error)
    }
}

如果要查找最近的公交站,可以先试试较低精度,例如 kCLLocationAccuracyKilometer;更高精度可能需要更长时间。Apple 也提醒,位置并非始终对小组件可用:小组件一段时间未显示后,系统可能停止向它提供位置。生成 timeline 时应设置超时,并准备好无位置时显示的内容,例如上次已知结果,或暂时无法获取位置的提示。Apple:Accessing location information in widgets

排查超时

  • 检查 App 的授权状态和 manager.isAuthorizedForWidgetUpdates。App 已获授权,不代表小组件扩展授权也已批准。
  • 确认系统位置服务已开启,并在设置中检查该 App 和小组件的位置访问选项。
  • 在 didFailWithError 中记录错误,以区分无授权、暂时无法确定位置等情况。还要检查 didUpdateLocations 是否收到回调,以及位置时间戳是否足够新。Apple:CLLocationManager
  • 检查创建 CLLocationManager 的线程是否有活动 RunLoop,并确保 delegate 一直被保留。Core Location 会将 delegate 回调发送到创建 manager 所在线程的 RunLoop。
  • 在实际使用场景中测试:添加小组件时允许或拒绝扩展授权、在设置中更改授权,以及小组件长时间未显示后再次出现。WidgetKit 会管理刷新,以及小组件处于可用状态的时间。

先将用途说明字符串移到包含小组件的 App 的 Info.plist,再确认用户已允许扩展授权。如果授权检查通过,但请求仍没有回调,可以根据 delegate 错误、RunLoop 状态,以及小组件当前是否能获取位置继续排查。现有信息无法确定超时的唯一原因。

备注:内容仅供参考。