Add weather feature with WeatherKit integration for mood entries
Fetch and display weather data (temp, condition, hi/lo, humidity) when users log a mood. Weather is stored as JSON on MoodEntryModel and shown as a card in EntryDetailView. Premium-gated with location permission prompt. Includes BGTask retry for failed fetches and full analytics. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
71
Shared/Services/LocationManager.swift
Normal file
71
Shared/Services/LocationManager.swift
Normal file
@@ -0,0 +1,71 @@
|
||||
//
|
||||
// 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<CLLocation, Error>?
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
return try await withCheckedThrowingContinuation { continuation in
|
||||
self.locationContinuation = continuation
|
||||
self.manager.requestLocation()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user