// // LocationManager.swift // Reflect // // CoreLocation wrapper with async/await for one-shot location requests. // import CoreLocation import os.log @MainActor final class LocationManager: NSObject { static let shared = LocationManager() private static let logger = Logger( subsystem: Bundle.main.bundleIdentifier ?? "com.88oakapps.reflect", category: "LocationManager" ) private let manager = CLLocationManager() private var locationContinuation: CheckedContinuation? var authorizationStatus: CLAuthorizationStatus { manager.authorizationStatus } private override init() { super.init() manager.delegate = self manager.desiredAccuracy = kCLLocationAccuracyKilometer } func requestAuthorization() { manager.requestWhenInUseAuthorization() } var currentLocation: CLLocation { get async throws { // Return last known location if recent enough (within 10 minutes) if let last = manager.location, abs(last.timestamp.timeIntervalSinceNow) < 600 { return last } // Pre-check authorization before requesting location switch manager.authorizationStatus { case .denied, .restricted: throw LocationError.permissionDenied case .notDetermined: manager.requestWhenInUseAuthorization() // Wait briefly for authorization response try await Task.sleep(for: .seconds(1)) if manager.authorizationStatus == .denied || manager.authorizationStatus == .restricted { throw LocationError.permissionDenied } default: break } // Request location with a 15-second timeout return try await withCheckedThrowingContinuation { continuation in self.locationContinuation = continuation self.manager.requestLocation() // Schedule timeout — safe because everything is @MainActor (no race) Task { @MainActor in try? await Task.sleep(for: .seconds(15)) guard self.locationContinuation != nil else { return } self.locationContinuation?.resume(throwing: LocationError.timeout) self.locationContinuation = nil } } } } // MARK: - Errors enum LocationError: LocalizedError { case permissionDenied case timeout var errorDescription: String? { switch self { case .permissionDenied: return "Location permission denied" case .timeout: return "Location request timed out" } } } } // MARK: - CLLocationManagerDelegate extension LocationManager: CLLocationManagerDelegate { nonisolated func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) { Task { @MainActor in guard let location = locations.first else { return } locationContinuation?.resume(returning: location) locationContinuation = nil } } nonisolated func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) { Task { @MainActor in Self.logger.error("Location request failed: \(error.localizedDescription)") locationContinuation?.resume(throwing: error) locationContinuation = nil } } }