From c5050df4454b94d200556a79e9968e9a034943aa Mon Sep 17 00:00:00 2001 From: FuelBoard Contributor Date: Tue, 11 Aug 2026 16:16:46 +0100 Subject: [PATCH] Live location tracking: continuous foreground updates (250m filter) + significant-change background monitoring; list/widget refresh and geofences follow the user, re-fetch on every move --- FuelBoard/ContentView.swift | 46 +++++++++++++++++++++++++++++++++---- 1 file changed, 42 insertions(+), 4 deletions(-) diff --git a/FuelBoard/ContentView.swift b/FuelBoard/ContentView.swift index e343fc2..7333f21 100644 --- a/FuelBoard/ContentView.swift +++ b/FuelBoard/ContentView.swift @@ -96,7 +96,7 @@ struct ContentView: View { .tabItem { Label("Alerts", systemImage: "bell.fill") } } .onAppear { - locationManager.requestUpdate() + locationManager.startForegroundTracking() monitor.update(stations: stations, favourites: refreshedFavourites, fuel: selectedFuel, radiusKM: alertsRadius) monitor.setEnabled(alertsEnabled) @@ -104,10 +104,12 @@ struct ContentView: View { } .onChange(of: scenePhase) { _, newPhase in if newPhase == .active { - locationManager.requestUpdate() + locationManager.startForegroundTracking() monitor.update(stations: stations, favourites: refreshedFavourites, fuel: selectedFuel, radiusKM: alertsRadius) Task { await refresh() } + } else { + locationManager.stopForegroundTracking() } } .onChange(of: locationManager.current) { _, newLocation in @@ -123,6 +125,9 @@ struct ContentView: View { monitor.setEnabled(newValue) monitor.update(stations: stations, favourites: refreshedFavourites, fuel: selectedFuel, radiusKM: alertsRadius) + if newValue { + locationManager.startBackgroundTracking() + } } .onChange(of: alertsRadius) { _, newValue in FuelStore.saveAlertsRadius(newValue) @@ -284,15 +289,48 @@ final class LocationManager: NSObject, ObservableObject, @preconcurrency CLLocat super.init() manager.delegate = self manager.desiredAccuracy = kCLLocationAccuracyHundredMeters + manager.distanceFilter = 250 // metres — re-fires while driving + manager.pausesLocationUpdatesAutomatically = true } - func requestUpdate() { + /// Continuous tracking while the app is in the foreground, so the list + /// re-fetches around the user's new position as they drive. + func startForegroundTracking() { switch manager.authorizationStatus { case .notDetermined: manager.requestWhenInUseAuthorization() manager.requestLocation() case .authorizedWhenInUse, .authorizedAlways: - manager.requestLocation() + manager.startUpdatingLocation() + default: + break + } + } + + /// One-shot fix (first launch / after permission grant). + func requestUpdate() { + manager.requestLocation() + } + + /// Background wake-ups on significant movement. Needs Always permission; + /// called once the user enables alerts so geofences + prices follow them. + func startBackgroundTracking() { + guard manager.authorizationStatus == .authorizedAlways else { return } + manager.startMonitoringSignificantLocationChanges() + } + + func stopForegroundTracking() { + // Significant-change monitoring keeps running in the background. + manager.stopUpdatingLocation() + } + + func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) { + switch manager.authorizationStatus { + case .authorizedAlways: + manager.startMonitoringSignificantLocationChanges() + manager.startUpdatingLocation() + case .authorizedWhenInUse: + manager.startUpdatingLocation() default: break }