95 lines
3.5 KiB
Swift
95 lines
3.5 KiB
Swift
import CoreLocation
|
|
import Combine
|
|
import Foundation
|
|
|
|
@MainActor
|
|
final class LocationProvider: NSObject, ObservableObject {
|
|
@Published private(set) var location: CLLocation?
|
|
@Published private(set) var authorizationStatus: CLAuthorizationStatus
|
|
@Published private(set) var errorMessage: String?
|
|
@Published private(set) var isLocating = false
|
|
|
|
private let manager: CLLocationManager
|
|
|
|
override init() {
|
|
let manager = CLLocationManager()
|
|
self.manager = manager
|
|
authorizationStatus = manager.authorizationStatus
|
|
super.init()
|
|
manager.delegate = self
|
|
manager.desiredAccuracy = kCLLocationAccuracyBest
|
|
}
|
|
|
|
func requestCurrentLocation() {
|
|
errorMessage = nil
|
|
switch manager.authorizationStatus {
|
|
case .notDetermined:
|
|
isLocating = true
|
|
manager.requestWhenInUseAuthorization()
|
|
case .authorizedAlways, .authorizedWhenInUse:
|
|
isLocating = true
|
|
manager.requestLocation()
|
|
case .denied, .restricted:
|
|
isLocating = false
|
|
errorMessage = "Доступ к геопозиции выключен. Можно выбрать точку на карте вручную."
|
|
@unknown default:
|
|
isLocating = false
|
|
errorMessage = "Не удалось определить статус доступа к геопозиции."
|
|
}
|
|
}
|
|
}
|
|
|
|
extension LocationProvider: CLLocationManagerDelegate {
|
|
nonisolated func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
|
|
let status = manager.authorizationStatus
|
|
Task { @MainActor [weak self] in
|
|
guard let self else { return }
|
|
authorizationStatus = status
|
|
switch status {
|
|
case .authorizedAlways, .authorizedWhenInUse:
|
|
isLocating = true
|
|
self.manager.requestLocation()
|
|
case .denied, .restricted:
|
|
isLocating = false
|
|
errorMessage = "Доступ к геопозиции выключен. Можно выбрать точку на карте вручную."
|
|
case .notDetermined:
|
|
break
|
|
@unknown default:
|
|
isLocating = false
|
|
}
|
|
}
|
|
}
|
|
|
|
nonisolated func locationManager(
|
|
_ manager: CLLocationManager,
|
|
didUpdateLocations locations: [CLLocation]
|
|
) {
|
|
guard let newest = locations.last else { return }
|
|
let latitude = newest.coordinate.latitude
|
|
let longitude = newest.coordinate.longitude
|
|
let altitude = newest.altitude
|
|
let horizontalAccuracy = newest.horizontalAccuracy
|
|
let verticalAccuracy = newest.verticalAccuracy
|
|
let timestamp = newest.timestamp
|
|
Task { @MainActor [weak self] in
|
|
self?.location = CLLocation(
|
|
coordinate: CLLocationCoordinate2D(latitude: latitude, longitude: longitude),
|
|
altitude: altitude,
|
|
horizontalAccuracy: horizontalAccuracy,
|
|
verticalAccuracy: verticalAccuracy,
|
|
timestamp: timestamp
|
|
)
|
|
self?.isLocating = false
|
|
self?.errorMessage = nil
|
|
}
|
|
}
|
|
|
|
nonisolated func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
|
|
let description = error.localizedDescription
|
|
Task { @MainActor [weak self] in
|
|
self?.isLocating = false
|
|
self?.errorMessage = "Не удалось получить текущую геопозицию: \(description)"
|
|
}
|
|
}
|
|
}
|