From e8a82ce5159e1ca2ad9822d041e62c2759d69998 Mon Sep 17 00:00:00 2001 From: FuelBoard Contributor Date: Tue, 11 Aug 2026 16:50:45 +0100 Subject: [PATCH] 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 --- FuelBoard/ContentView.swift | 2 +- FuelBoard/ProximityMonitor.swift | 22 ++++++++++++++++++---- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/FuelBoard/ContentView.swift b/FuelBoard/ContentView.swift index 7333f21..b19c735 100644 --- a/FuelBoard/ContentView.swift +++ b/FuelBoard/ContentView.swift @@ -5,7 +5,7 @@ import WidgetKit struct ContentView: View { @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 sortMode: SortMode = FuelStore.loadSortMode() @State private var stationLimit: Int = FuelStore.loadStationLimit() diff --git a/FuelBoard/ProximityMonitor.swift b/FuelBoard/ProximityMonitor.swift index 5902d1a..1f84d3f 100644 --- a/FuelBoard/ProximityMonitor.swift +++ b/FuelBoard/ProximityMonitor.swift @@ -25,12 +25,21 @@ final class ProximityMonitor: NSObject, ObservableObject, @preconcurrency CLLoca manager.delegate = self manager.desiredAccuracy = kCLLocationAccuracyHundredMeters 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. func update(stations: [FuelStation], favourites: [FuelStation], fuel: FuelType, radiusKM: Double) { - self.stations = stations - self.favourites = favourites + // Fall back to the shared cache when called before the first fetch + // completes (launch, background region-event wake). + self.stations = stations.isEmpty ? FuelStore.loadStations() : stations + self.favourites = favourites.isEmpty ? FuelStore.loadFavourites() : favourites self.fuel = fuel self.radiusKM = radiusKM @@ -78,6 +87,8 @@ final class ProximityMonitor: NSObject, ObservableObject, @preconcurrency CLLoca monitoredStationIDs = [] } else { 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) { 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 } // 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]! }), 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() } catch { // Silent — geofence state stays valid for next entry.