You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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)")
    }
}

关键改动说明

  1. 用CheckedContinuation实现异步等待:把CLLocationManager的回调逻辑转换成async函数,让perform可以通过await等待位置结果,彻底解决异步竞态问题
  2. 保留LocationManager实例:将locationManager改为IntentHelper的实例变量,避免被提前释放,确保能正常接收delegate回调
  3. 主动发起位置更新:调用startUpdatingLocation()触发实时位置获取,不再依赖不可靠的缓存位置
  4. 完善错误处理:覆盖权限不足、获取失败等场景,返回更友好的用户提示

内容的提问来源于stack exchange,提问作者David

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.11 00:10:52