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

寻求位置权限授予后填充ViewModel的正确实现方式

解决方案

核心思路

要实现权限授予后自动加载地图标注,关键是让LocationManager和VenuesViewModel联动,确保权限通过且获取到有效位置后再触发数据加载,同时避免GeoFire的坐标精度错误。

步骤1:改造LocationManager,发布状态与位置

给LocationManager添加@Published属性,对外暴露权限状态和当前位置的变化:

import CoreLocation
import Combine

class LocationManager: NSObject, ObservableObject, CLLocationManagerDelegate {
    let manager = CLLocationManager()
    @Published var authorizationStatus: CLAuthorizationStatus = .notDetermined
    @Published var currentLocation: CLLocation?
    
    override init() {
        super.init()
        manager.delegate = self
        manager.requestWhenInUseAuthorization()
        manager.startUpdatingLocation()
    }
    
    func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
        authorizationStatus = manager.authorizationStatus
        // 权限允许时启动位置更新
        if [.authorizedWhenInUse, .authorizedAlways].contains(authorizationStatus) {
            manager.startUpdatingLocation()
        }
    }
    
    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        guard let latestLocation = locations.last else { return }
        currentLocation = latestLocation
        manager.stopUpdatingLocation() // 获取一次位置后停止,按需调整
    }
}

步骤2:让ViewModel监听LocationManager的变化

通过依赖注入将LocationManager传入ViewModel,监听权限和位置的组合变化,满足条件时加载数据,同时处理坐标精度问题:

import Foundation
import Combine
import GeoFire
import FirebaseFirestore

class VenuesViewModel: ObservableObject {
    @Published var venues: [Venue] = []
    private let geoFire: GeoFire
    private var cancellables = Set<AnyCancellable>()
    
    init(locationManager: LocationManager) {
        let db = Firestore.firestore()
        geoFire = GeoFire(firebaseRef: db.collection("venues"))
        
        // 同时监听权限状态和当前位置
        locationManager.$authorizationStatus
            .combineLatest(locationManager.$currentLocation)
            .sink { [weak self] status, location in
                guard let self = self,
                      [.authorizedWhenInUse, .authorizedAlways].contains(status),
                      let validLocation = location else { return }
                
                self.loadVenues(at: validLocation)
            }
            .store(in: &cancellables)
    }
    
    private func loadVenues(at location: CLLocation) {
        // 限制经纬度小数位到6位,避免GeoFire的精度错误
        let fixedLocation = CLLocation(
            latitude: location.coordinate.latitude.roundTo(6),
            longitude: location.coordinate.longitude.roundTo(6)
        )
        
        // 按范围查询标注
        let radius = 1000.0 // 1公里范围,按需调整
        geoFire.query(at: fixedLocation, withRadius: radius).observe(.keyEntered) { key, _ in
            // 从Firestore获取对应标注数据
            Firestore.firestore().collection("venues").document(key).getDocument { snapshot, _ in
                guard let snapshot = snapshot,
                      let venue = try? snapshot.data(as: Venue.self) else { return }
                
                DispatchQueue.main.async {
                    self.venues.append(venue)
                }
            }
        }
    }
}

// 辅助方法:限制小数位
extension Double {
    func roundTo(_ places: Int) -> Double {
        let divisor = pow(10.0, Double(places))
        return (self * divisor).rounded() / divisor
    }
}

步骤3:调整环境对象注入顺序

先初始化LocationManager,再传入ViewModel,确保依赖关系正确:

var body: some Scene {
    WindowGroup {
        let locationManager = LocationManager()
        let venuesVM = VenuesViewModel(locationManager: locationManager)
        ContentView()
            .environmentObject(locationManager)
            .environmentObject(venuesVM)
    }
}

错误原因说明

你遇到的"Precision must be less than 23"错误,是因为GeoFire对坐标精度有要求(经纬度小数位不能过多)。直接在locationManagerDidChangeAuthorization调用加载函数时,可能还未获取到有效位置,传递了无效/精度过高的坐标导致报错。现在的方案确保只有获取到有效位置并修正精度后,才调用GeoFire查询。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 20:13:14