// // LocationProvider.swift // suixinkan // import CoreLocation import Foundation /// 统一定位服务,封装高德 `AMapLocationManager`,对齐 Android `LocationProvider`。 final class LocationProvider: NSObject, LocationProviding { static let shared = LocationProvider() private let permissionManager = CLLocationManager() private let geocoder: LocationGeocoder init(geocoder: LocationGeocoder = .shared) { self.geocoder = geocoder super.init() permissionManager.desiredAccuracy = kCLLocationAccuracyBest } func requestSnapshot() async throws -> HomeLocationSnapshot { try AMapBootstrap.requireConfigured() try await ensureLocationPermission() let manager = makeLocationManager( desiredAccuracy: kCLLocationAccuracyBest, locatingWithReGeocode: true ) return try await withCheckedThrowingContinuation { continuation in let started = manager.requestLocation(withReGeocode: true) { location, regeocode, error in if let error { continuation.resume(throwing: error) return } guard let location else { continuation.resume(throwing: LocationProviderError.locationFailed) return } let address = regeocode?.formattedAddress ?? "" continuation.resume(returning: HomeLocationSnapshot( latitude: location.coordinate.latitude, longitude: location.coordinate.longitude, address: address )) } if !started { continuation.resume(throwing: LocationProviderError.locationFailed) } } } func requestCoordinate(desiredAccuracy: CLLocationAccuracy) async throws -> CLLocationCoordinate2D { try AMapBootstrap.requireConfigured() try await ensureLocationPermission() let manager = makeLocationManager( desiredAccuracy: desiredAccuracy, locatingWithReGeocode: false ) return try await withCheckedThrowingContinuation { continuation in let started = manager.requestLocation(withReGeocode: false) { location, _, error in if let error { continuation.resume(throwing: error) return } guard let location else { continuation.resume(throwing: LocationProviderError.locationFailed) return } continuation.resume(returning: location.coordinate) } if !started { continuation.resume(throwing: LocationProviderError.locationFailed) } } } func reverseGeocode(latitude: Double, longitude: Double) async -> String { await geocoder.reverseGeocode(latitude: latitude, longitude: longitude) } /// 请求系统定位权限,首次弹窗后等待用户响应。 func ensureLocationPermission() async throws { let initialStatus = permissionManager.authorizationStatus if initialStatus == .notDetermined { permissionManager.requestWhenInUseAuthorization() try await Task.sleep(nanoseconds: 500_000_000) } let status = permissionManager.authorizationStatus guard status == .authorizedWhenInUse || status == .authorizedAlways else { throw LocationProviderError.permissionDenied } } private func makeLocationManager( desiredAccuracy: CLLocationAccuracy, locatingWithReGeocode: Bool ) -> AMapLocationManager { let manager = AMapLocationManager() manager.desiredAccuracy = desiredAccuracy manager.locationTimeout = 30 manager.reGeocodeTimeout = 10 manager.locatingWithReGeocode = locatingWithReGeocode return manager } }