AutoCat/AutoCatCore/Services/LocationService/LocationService.swift

113 lines
3.3 KiB
Swift

//
// LocationService.swift
// AutoCatCore
//
// Created by Selim Mustafaev on 31.07.2024.
// Copyright © 2024 Selim Mustafaev. All rights reserved.
//
import CoreLocation
import SwiftLocation
@MainActor
public final class LocationService {
let geocoder: GeocoderProtocol
let locationManager: SwiftLocationProtocol
let settingsService: SettingsServiceProtocol
private var eventTask: Task<VehicleEventDto,Error>?
private(set) public var lastEvent: VehicleEventDto?
public init(geocoder: GeocoderProtocol,
locationManager: SwiftLocationProtocol,
settingsService: SettingsServiceProtocol) {
self.geocoder = geocoder
self.locationManager = locationManager
self.settingsService = settingsService
}
private func checkPermissions() async throws {
switch locationManager.authorizationStatus {
case .authorizedWhenInUse, .authorizedAlways:
break
case .notDetermined:
_ = try await locationManager.requestPermission(.always)
try await checkPermissions()
case .denied:
throw CLError(.denied)
default:
throw LocationError.permission
}
}
private func requestLocation() async throws -> VehicleEventDto {
try await checkPermissions()
let locationEvent = try await locationManager.requestLocation(accuracy: nil, timeout: 20)
guard let coordinate = locationEvent.location?.coordinate else {
throw LocationError.generic
}
return VehicleEventDto(lat: coordinate.latitude, lon: coordinate.longitude, addedBy: settingsService.user.email)
}
func setLastEvent(_ event: VehicleEventDto) {
lastEvent = event
}
}
extension LocationService: LocationServiceProtocol {
public func getAddressForLocation(latitude: Double, longitude: Double) async throws -> String {
let location = CLLocation(latitude: latitude, longitude: longitude)
let placemarks = try await geocoder.reverseGeocodeLocation(location)
if let name = placemarks.first?.name {
return name
} else {
throw LocationError.reverseGeocode
}
}
@discardableResult
public func requestCurrentLocation() async throws -> VehicleEventDto {
if let eventTask {
return try await eventTask.value
} else {
let task = Task {
let location = try await requestLocation()
eventTask = nil
lastEvent = location
return location
}
eventTask = task
return try await task.value
}
}
public func getRecentLocation() async throws -> VehicleEventDto {
var event: VehicleEventDto
if let lastEvent, Date().timeIntervalSince1970 - lastEvent.date < 100 {
event = lastEvent
} else {
event = try await requestCurrentLocation()
}
event.address = try? await getAddressForLocation(latitude: event.latitude, longitude: event.longitude)
return event
}
public func resetLastEvent() {
lastEvent = nil
}
}