Files
suixinkan_uikit/suixinkan/Features/Location/LocationProvider.swift

110 lines
3.9 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//
// 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
}
}