mirror of
https://github.com/jayanezj/traccar-client-macos.git
synced 2026-11-05 03:00:46 +01:00
feat: add native local location capture (#4)
Add explicit permission handling, independent continuous and single capture, location status and deterministic CI coverage. Final decisions and validation: https://github.com/jayanezj/traccar-client-macos/issues/4#issuecomment-6399189792
This commit is contained in:
18 files changed
+1030
-38
No files matched your search
@@ -0,0 +1,102 @@
|
||||
import CoreLocation
|
||||
import Foundation
|
||||
|
||||
enum LocationPermission: String {
|
||||
case notDetermined = "Not requested"
|
||||
case authorized = "Authorized"
|
||||
case denied = "Denied"
|
||||
case restricted = "Restricted"
|
||||
|
||||
init(_ status: CLAuthorizationStatus) {
|
||||
switch status {
|
||||
case .authorizedAlways: self = .authorized
|
||||
case .notDetermined: self = .notDetermined
|
||||
case .denied: self = .denied
|
||||
default: self = .restricted
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum LocationServices: String {
|
||||
case checking = "Checking"
|
||||
case enabled = "Enabled"
|
||||
case disabled = "Disabled"
|
||||
}
|
||||
|
||||
struct LocationAvailability: Equatable {
|
||||
var permission: LocationPermission
|
||||
var services: LocationServices
|
||||
|
||||
var canCapture: Bool { permission == .authorized && services == .enabled }
|
||||
}
|
||||
|
||||
struct LocationFix: Equatable {
|
||||
let latitude: Double
|
||||
let longitude: Double
|
||||
let timestamp: Date
|
||||
let horizontalAccuracy: Double
|
||||
let altitude: Double?
|
||||
let speed: Double?
|
||||
let course: Double?
|
||||
|
||||
init?(_ location: CLLocation) {
|
||||
guard CLLocationCoordinate2DIsValid(location.coordinate),
|
||||
location.horizontalAccuracy.isFinite, location.horizontalAccuracy >= 0,
|
||||
location.timestamp.timeIntervalSinceReferenceDate.isFinite else { return nil }
|
||||
latitude = location.coordinate.latitude
|
||||
longitude = location.coordinate.longitude
|
||||
timestamp = location.timestamp
|
||||
horizontalAccuracy = location.horizontalAccuracy
|
||||
altitude = location.verticalAccuracy >= 0 && location.altitude.isFinite ? location.altitude : nil
|
||||
speed = location.speed >= 0 && location.speed.isFinite ? location.speed : nil
|
||||
course = (0..<360).contains(location.course) && location.course.isFinite ? location.course : nil
|
||||
}
|
||||
}
|
||||
|
||||
enum LocationFailure: Equatable {
|
||||
case permissionDenied
|
||||
case unavailable
|
||||
case other(Int)
|
||||
|
||||
var message: String {
|
||||
switch self {
|
||||
case .permissionDenied: "Location access is denied. Review Location Services in System Settings."
|
||||
case .unavailable: "No location is available. Try again when Location Services can obtain a fix."
|
||||
case .other(let code): "Location could not be obtained (error \(code))."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum LocationRequestKind { case continuous, single }
|
||||
enum LocationFixOrigin: String {
|
||||
case continuous = "Continuous capture"
|
||||
case single = "Single capture"
|
||||
case lastKnown = "Last-known location; no new fix"
|
||||
}
|
||||
|
||||
enum SingleCaptureStatus: String {
|
||||
case idle = "Idle"
|
||||
case waiting = "Waiting for location"
|
||||
case received = "Location received"
|
||||
case lastKnown = "Last-known location used; no new fix"
|
||||
case failed = "No location obtained"
|
||||
case cancelled = "Cancelled"
|
||||
}
|
||||
|
||||
enum LocationEvent {
|
||||
case availability(LocationAvailability)
|
||||
case locations(LocationRequestKind, request: Int, fixes: [LocationFix])
|
||||
case failure(LocationRequestKind, request: Int, reason: LocationFailure, fallback: LocationFix?)
|
||||
}
|
||||
|
||||
@MainActor
|
||||
protocol LocationProviding: AnyObject {
|
||||
var availability: LocationAvailability { get }
|
||||
var onEvent: ((LocationEvent) -> Void)? { get set }
|
||||
func refreshAvailability()
|
||||
func requestAuthorization()
|
||||
func startContinuous(accuracy: LocationAccuracy, request: Int)
|
||||
func stopContinuous()
|
||||
func requestSingle(accuracy: LocationAccuracy, request: Int)
|
||||
func cancelSingle()
|
||||
}
|
||||
Reference in new issue
Block a user