iOS 16 App Intents中如何等待获取用户当前位置?
解决AppIntents中等待获取用户位置的问题
你的代码核心问题有三个:
- LocationManager被提前释放:
getUserLocation里创建的locationManager是局部变量,函数执行完毕就会被销毁,根本无法触发delegate回调获取位置 - 未主动发起位置更新:直接读取
locationManager.location只能拿到缓存位置,多数时候还没完成实时位置获取就返回nil - 无异步等待逻辑:调用
getUserLocation后立刻读取currentUserCoords,此时位置还在获取中,必然抛出错误
以下是修正后的完整实现:
重构IntentHelper(用Async/Await封装位置获取)
import CoreLocation class IntentHelper: NSObject, CLLocationManagerDelegate { static let shared = IntentHelper() private let locationManager = CLLocationManager() private var locationContinuation: CheckedContinuation<CLLocationCoordinate2D, Error>? private override init() { super.init() locationManager.delegate = self } func getUserLocation() async throws -> CLLocationCoordinate2D { // 校验权限状态(确保权限配置生效) let status = locationManager.authorizationStatus guard status == .authorizedWhenInUse || status == .authorizedAlways else { throw IntentErrors.locationPermissionDenied } return try await withCheckedThrowingContinuation { continuation in locationContinuation = continuation // 主动发起实时位置更新请求 locationManager.startUpdatingLocation() } } // MARK: - CLLocationManagerDelegate func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) { guard let latestLocation = locations.last, !latestLocation.coordinate.isValid else { return } // 获取到有效位置后停止更新 manager.stopUpdatingLocation() locationContinuation?.resume(returning: latestLocation.coordinate) locationContinuation = nil } func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) { manager.stopUpdatingLocation() locationContinuation?.resume(throwing: error) locationContinuation = nil } // 处理权限变更的兜底逻辑 func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) { guard let continuation = locationContinuation else { return } let status = manager.authorizationStatus if status == .denied || status == .restricted { continuation.resume(throwing: IntentErrors.locationPermissionDenied) locationContinuation = nil } } } // 自定义错误枚举 enum IntentErrors: Error { case locationProblem case locationPermissionDenied } // 扩展判断坐标是否有效 extension CLLocationCoordinate2D { var isValid: Bool { return latitude >= -90 && latitude <= 90 && longitude >= -180 && longitude <= 180 } }
修改Intent的perform方法
func perform() async throws -> some IntentResult { do { let userCoords = try await IntentHelper.shared.getUserLocation() return .result(dialog: "成功!当前坐标:\(userCoords.latitude), \(userCoords.longitude)") { IntentSuccessView() } } catch IntentErrors.locationPermissionDenied { return .result(dialog: "位置权限未开启,请在设置中授权") } catch { return .result(dialog: "获取位置失败:\(error.localizedDescription)") } }
关键改动说明
- 用CheckedContinuation实现异步等待:把CLLocationManager的回调逻辑转换成async函数,让
perform可以通过await等待位置结果,彻底解决异步竞态问题 - 保留LocationManager实例:将
locationManager改为IntentHelper的实例变量,避免被提前释放,确保能正常接收delegate回调 - 主动发起位置更新:调用
startUpdatingLocation()触发实时位置获取,不再依赖不可靠的缓存位置 - 完善错误处理:覆盖权限不足、获取失败等场景,返回更友好的用户提示
内容的提问来源于stack exchange,提问作者David
相关产品推荐
相关产品推荐

