Fix widget distance menu + geofence notification chain
Widget distance menu: the Distance picker only makes sense for Cheapest ordering. It was hidden for Favourites but Closest still showed it (regressed when the per-widget Distance picker was added). parameterSummary now nests When clauses: Distance visible for Cheapest only; Closest and Favourites hide it. Notifications: three compounding defects kept real geofence alerts from ever firing while the app was suspended: 1. Always permission was never properly requested. requestPermissions fired WhenInUse and Always back-to-back; iOS ignores the second call while the first prompt is pending, leaving the app stuck on WhenInUse — and region entries are never delivered in the background. Added locationManagerDidChangeAuthorization to ProximityMonitor: escalate WhenInUse -> Always, and re-register geofences on grant (regions registered under WhenInUse-only won't deliver in the background). 2. The 18-region window was frozen in the background. Re-registration lived in SwiftUI .onChange(of: locationManager.current), which never runs while suspended. Added a delegate hook (LocationManager.onLocationUpdate) fired from didUpdateLocations on every fix including background significant-change wake-ups; the app wires it to re-register geofences around the new position. 3. Region-registration failures were invisible. monitoringDidFailFor was never implemented, so a failed startMonitoring (region budget, auth, radius) silently stopped alerts. Now surfaced as monitor.lastRegionError and shown in Settings -> Debug; cleared on the next successful registration.
This commit is contained in:
@@ -179,6 +179,16 @@ struct ContentView: View {
|
||||
// network fetch start once it's done.
|
||||
if FuelStore.loadHasCompletedOnboarding() {
|
||||
locationManager.startForegroundTracking()
|
||||
// Geofences must follow the user even in the background:
|
||||
// wire the delegate hook (fires on every fix incl. background
|
||||
// significant-change wake-ups) to re-register the region
|
||||
// window around the new position. SwiftUI onChange alone
|
||||
// never runs while suspended, so alerts would otherwise stay
|
||||
// frozen around the last foreground fix.
|
||||
locationManager.onLocationUpdate = { [weak monitor] in
|
||||
monitor?.update(stations: stations, favourites: refreshedFavourites,
|
||||
fuel: alertsFuel, radiusKM: alertsRadius)
|
||||
}
|
||||
monitor.update(stations: stations, favourites: refreshedFavourites,
|
||||
fuel: alertsFuel, radiusKM: alertsRadius)
|
||||
monitor.setEnabled(alertsEnabled)
|
||||
@@ -277,7 +287,8 @@ struct ContentView: View {
|
||||
onRefreshDebugStatus: { monitor.refreshDebugStatus() },
|
||||
onShowOnboarding: { showOnboarding = true },
|
||||
debugStatus: monitor.debugStatus,
|
||||
appCheapest: appCheapestStatus
|
||||
appCheapest: appCheapestStatus,
|
||||
regionError: monitor.lastRegionError
|
||||
)
|
||||
.tabItem { Label("Settings", systemImage: "gearshape.fill") }
|
||||
}
|
||||
@@ -457,6 +468,12 @@ struct StationRow: View {
|
||||
@MainActor
|
||||
final class LocationManager: NSObject, ObservableObject, @preconcurrency CLLocationManagerDelegate {
|
||||
@Published var current: Coordinate?
|
||||
/// Called after every location fix (foreground AND background wake-ups).
|
||||
/// The app uses it to re-register geofences around the new position —
|
||||
/// SwiftUI's `.onChange` never runs in the background, so this delegate
|
||||
/// hook is the only path that keeps the 18-region window following the
|
||||
/// user while driving with the app suspended.
|
||||
var onLocationUpdate: (() -> Void)?
|
||||
private let manager = CLLocationManager()
|
||||
|
||||
override init() {
|
||||
@@ -522,6 +539,10 @@ final class LocationManager: NSObject, ObservableObject, @preconcurrency CLLocat
|
||||
lastWidgetReload = now
|
||||
WidgetCenter.shared.reloadAllTimelines()
|
||||
}
|
||||
// Re-register geofences around the new position. Also fires on
|
||||
// background significant-change wake-ups, which SwiftUI onChange
|
||||
// never sees — this is what keeps alerts working while driving.
|
||||
onLocationUpdate?()
|
||||
}
|
||||
|
||||
private var lastWidgetReload = Date.distantPast
|
||||
|
||||
Reference in New Issue
Block a user