98b7600797
对标安卓正式版、对接 app-server(8770)的 SwiftUI 客户端。iOS 无障碍受限、无比价核心,做自成一体:美团 CPS 导购 + 福利(签到/任务/金币)+ 查看(比价记录/省钱战绩)。 功能完整 M0-M5:脚手架、短信登录(JWT+Keychain+401 自动刷新)、首页券 feed 导购、比价记录+省钱战绩、福利、我的(资料/反馈/设置/注销)。 单 target、0 三方依赖、XcodeGen 生成工程;Debug/Release 双 plist 分流 ATS。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
74 lines
2.8 KiB
Swift
74 lines
2.8 KiB
Swift
import CoreLocation
|
|
|
|
/// CoreLocation 的极简 async 封装:请求一次"使用期间"定位。
|
|
/// 无权限/拒绝/失败都返 nil,调用方用默认坐标兜底(对齐安卓首页定位失败兜底天安门)。
|
|
@MainActor
|
|
final class LocationManager: NSObject, CLLocationManagerDelegate {
|
|
private let manager = CLLocationManager()
|
|
private var authContinuation: CheckedContinuation<Bool, Never>?
|
|
private var locContinuation: CheckedContinuation<CLLocationCoordinate2D?, Never>?
|
|
|
|
override init() {
|
|
super.init()
|
|
manager.delegate = self
|
|
manager.desiredAccuracy = kCLLocationAccuracyHundredMeters
|
|
}
|
|
|
|
/// 请求一次定位。notDetermined 先弹权限,授权后取一次坐标;拒绝/失败返 nil。
|
|
func requestOnce() async -> CLLocationCoordinate2D? {
|
|
// 防重入:Tab 切换会让 HomeView 的 .task 重启二次调用,避免覆盖 continuation 导致前一次永久挂起
|
|
guard authContinuation == nil, locContinuation == nil else { return nil }
|
|
switch manager.authorizationStatus {
|
|
case .denied, .restricted:
|
|
return nil
|
|
case .notDetermined:
|
|
guard await requestAuthorization() else { return nil }
|
|
default:
|
|
break
|
|
}
|
|
return await fetchLocation()
|
|
}
|
|
|
|
private func requestAuthorization() async -> Bool {
|
|
await withCheckedContinuation { cont in
|
|
authContinuation = cont
|
|
manager.requestWhenInUseAuthorization()
|
|
}
|
|
}
|
|
|
|
private func fetchLocation() async -> CLLocationCoordinate2D? {
|
|
await withCheckedContinuation { cont in
|
|
locContinuation = cont
|
|
manager.requestLocation()
|
|
}
|
|
}
|
|
|
|
// MARK: - CLLocationManagerDelegate(回调可能非主线程,跳回 MainActor;continuation 置 nil 防重复 resume)
|
|
|
|
nonisolated func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
|
|
let status = manager.authorizationStatus
|
|
Task { @MainActor in
|
|
guard let cont = authContinuation else { return } // init 时的初始回调:cont 还没设,忽略
|
|
authContinuation = nil
|
|
cont.resume(returning: status == .authorizedWhenInUse || status == .authorizedAlways)
|
|
}
|
|
}
|
|
|
|
nonisolated func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
|
|
let coord = locations.first?.coordinate
|
|
Task { @MainActor in
|
|
guard let cont = locContinuation else { return }
|
|
locContinuation = nil
|
|
cont.resume(returning: coord)
|
|
}
|
|
}
|
|
|
|
nonisolated func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
|
|
Task { @MainActor in
|
|
guard let cont = locContinuation else { return }
|
|
locContinuation = nil
|
|
cont.resume(returning: nil)
|
|
}
|
|
}
|
|
}
|