118 lines
3.4 KiB
Swift
118 lines
3.4 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: await 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)
|
|
event.id = UUID().uuidString
|
|
return event
|
|
}
|
|
|
|
public func resetLastEvent() {
|
|
|
|
lastEvent = nil
|
|
}
|
|
}
|