Cache-first refresh policy: auto-fetch at most twice a day (12h gate), no fetch on launch/foreground/location/fuel-switch, pull-to-refresh forces; geofences still follow location

This commit is contained in:
FuelBoard Contributor
2026-08-11 17:43:11 +01:00
parent 5a08a3a2fa
commit 8230daa249
3 changed files with 46 additions and 7 deletions
+17 -7
View File
@@ -87,7 +87,8 @@ struct ContentView: View {
location: location,
radiusKM: alertsRadius,
favouriteIDs: favouriteIDs,
onToggleFavourite: toggleFavourite
onToggleFavourite: toggleFavourite,
onRefresh: { await refresh(force: true) }
)
.tabItem { Label("Stations", systemImage: "fuelpump.fill") }
@@ -113,6 +114,7 @@ struct ContentView: View {
monitor.update(stations: stations, favourites: refreshedFavourites,
fuel: selectedFuel, radiusKM: alertsRadius)
monitor.setEnabled(alertsEnabled)
// Refresh only when the cache is stale (twice-a-day policy).
Task { await refresh() }
}
.onChange(of: scenePhase) { _, newPhase in
@@ -120,7 +122,7 @@ struct ContentView: View {
locationManager.startForegroundTracking()
monitor.update(stations: stations, favourites: refreshedFavourites,
fuel: selectedFuel, radiusKM: alertsRadius)
Task { await refresh() }
// No network fetch on foreground pull-to-refresh is the override.
} else {
locationManager.stopForegroundTracking()
}
@@ -130,13 +132,15 @@ struct ContentView: View {
location = newLocation
FuelStore.saveLocation(lat: newLocation.lat, lng: newLocation.lng)
WidgetCenter.shared.reloadAllTimelines()
Task { await refresh() }
// Geofences follow the user's position, but the station list is
// NOT re-fetched on every movement (cached, twice-a-day policy).
monitor.update(stations: stations, favourites: refreshedFavourites,
fuel: selectedFuel, radiusKM: alertsRadius)
}
}
.onChange(of: selectedFuel) { _, _ in
// Re-fetch so the station set matches the selected fuel (relay
// filters by grade); all prices still come back in one response.
Task { await refresh() }
// No re-fetch needed one response carries E5/E10/DIESEL prices.
WidgetCenter.shared.reloadAllTimelines()
}
.onChange(of: alertsEnabled) { _, newValue in
FuelStore.saveAlertsEnabled(newValue)
@@ -166,13 +170,19 @@ struct ContentView: View {
fuel: selectedFuel, radiusKM: alertsRadius)
}
private func refresh() async {
/// Fetches fresh prices, but only when the cache is stale unless
/// `force` is true (pull-to-refresh is the manual override).
private func refresh(force: Bool = false) async {
if !force, FuelStore.isCacheFresh {
return // data already fresh skip network entirely
}
isLoading = true
defer { isLoading = false }
do {
let fetched = try await FuelPriceProvider.active.fetchStations(near: location?.lat, lng: location?.lng, fuel: selectedFuel)
stations = fetched
FuelStore.saveStations(fetched)
FuelStore.saveLastRefresh()
WidgetCenter.shared.reloadAllTimelines()
statusMessage = "Loaded \(fetched.count) stations · \(Date().formatted(date: .omitted, time: .shortened))"
} catch {
+5
View File
@@ -14,6 +14,7 @@ struct StationsView: View {
let radiusKM: Double
let favouriteIDs: Set<String>
var onToggleFavourite: (FuelStation) -> Void = { _ in }
var onRefresh: () async -> Void = {}
var body: some View {
NavigationStack {
@@ -130,6 +131,10 @@ struct StationsView: View {
}
}
}
.refreshable {
// Manual override for the twice-a-day cache policy.
await onRefresh()
}
.navigationTitle("FuelBoard")
}
}