Preload cached stations at launch (all tabs populate instantly); alert handler falls back to cache on background wake, restores enabled/fuel/radius in monitor init, re-registers geofences on setEnabled

This commit is contained in:
FuelBoard Contributor
2026-08-11 16:50:45 +01:00
parent c5050df445
commit e8a82ce515
2 changed files with 19 additions and 5 deletions
+1 -1
View File
@@ -5,7 +5,7 @@ import WidgetKit
struct ContentView: View { struct ContentView: View {
@Environment(\.scenePhase) private var scenePhase @Environment(\.scenePhase) private var scenePhase
@State private var stations: [FuelStation] = [] @State private var stations: [FuelStation] = FuelStore.loadStations()
@State private var selectedFuel: FuelType = FuelStore.loadSelectedFuel() @State private var selectedFuel: FuelType = FuelStore.loadSelectedFuel()
@State private var sortMode: SortMode = FuelStore.loadSortMode() @State private var sortMode: SortMode = FuelStore.loadSortMode()
@State private var stationLimit: Int = FuelStore.loadStationLimit() @State private var stationLimit: Int = FuelStore.loadStationLimit()
+18 -4
View File
@@ -25,12 +25,21 @@ final class ProximityMonitor: NSObject, ObservableObject, @preconcurrency CLLoca
manager.delegate = self manager.delegate = self
manager.desiredAccuracy = kCLLocationAccuracyHundredMeters manager.desiredAccuracy = kCLLocationAccuracyHundredMeters
manager.pausesLocationUpdatesAutomatically = true manager.pausesLocationUpdatesAutomatically = true
// Restore persisted state on background relaunch so region events work
// even before the view fully appears.
enabled = FuelStore.loadAlertsEnabled()
fuel = FuelStore.loadSelectedFuel()
radiusKM = FuelStore.loadAlertsRadius()
stations = FuelStore.loadStations()
favourites = FuelStore.loadFavourites()
} }
/// Re-registers geofences. Call whenever stations/favourites/settings change. /// Re-registers geofences. Call whenever stations/favourites/settings change.
func update(stations: [FuelStation], favourites: [FuelStation], fuel: FuelType, radiusKM: Double) { func update(stations: [FuelStation], favourites: [FuelStation], fuel: FuelType, radiusKM: Double) {
self.stations = stations // Fall back to the shared cache when called before the first fetch
self.favourites = favourites // completes (launch, background region-event wake).
self.stations = stations.isEmpty ? FuelStore.loadStations() : stations
self.favourites = favourites.isEmpty ? FuelStore.loadFavourites() : favourites
self.fuel = fuel self.fuel = fuel
self.radiusKM = radiusKM self.radiusKM = radiusKM
@@ -78,6 +87,8 @@ final class ProximityMonitor: NSObject, ObservableObject, @preconcurrency CLLoca
monitoredStationIDs = [] monitoredStationIDs = []
} else { } else {
requestPermissions() requestPermissions()
// Re-register geofences from restored cache immediately.
update(stations: stations, favourites: favourites, fuel: fuel, radiusKM: radiusKM)
} }
} }
@@ -111,6 +122,9 @@ final class ProximityMonitor: NSObject, ObservableObject, @preconcurrency CLLoca
private func handleEntry(_ region: CLRegion) { private func handleEntry(_ region: CLRegion) {
let stationID = region.identifier let stationID = region.identifier
// Fall back to cached data on background wake (stations may not be
// fetched yet when the app is relaunched by a region event).
if stations.isEmpty { stations = FuelStore.loadStations() }
guard let station = stations.first(where: { $0.id == stationID }) else { return } guard let station = stations.first(where: { $0.id == stationID }) else { return }
// Dedup: one alert per station per hour. // Dedup: one alert per station per hour.
@@ -128,9 +142,9 @@ final class ProximityMonitor: NSObject, ObservableObject, @preconcurrency CLLoca
} }
guard let cheapest = withinRadius.min(by: { $0.prices[fuel]! < $1.prices[fuel]! }), guard let cheapest = withinRadius.min(by: { $0.prices[fuel]! < $1.prices[fuel]! }),
cheapest.id == station.id, cheapest.id == station.id,
let price = station.prices[fuel] else { return } let price = cheapest.prices[fuel] else { return }
fireAlert(for: station, price: price) fireAlert(for: cheapest, price: price)
lastNotified[stationID] = Date() lastNotified[stationID] = Date()
} catch { } catch {
// Silent geofence state stays valid for next entry. // Silent geofence state stays valid for next entry.