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
检查授权配置
-
在 Widget Extension 的
Info.plist中保留:<key>NSWidgetWantsLocation</key> <true/> -
在包含小组件的 App 的
Info.plist中,配置与授权方式对应的用途说明。若 App 请求 When In Use 授权,至少应提供NSLocationWhenInUseUsageDescription。只有确实需要 Always 授权时,才配置并请求相应的 Always 权限。 -
由 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 状态,以及小组件当前是否能获取位置继续排查。现有信息无法确定超时的唯一原因。
备注:内容仅供参考。
