Alerts: trigger on approach — geofence radius = alert radius

- Region radius changed from fixed 300 m forecourt fence to the alert
  radius (the Alerts-tab slider), clamped to the device's
  maximumRegionMonitoringDistance.
- Notification now fires when you ENTER the winner's circle while
  approaching, not when you reach the forecourt.
- Cheapest-gate unchanged: still only fires when the entered station is
  the cheapest within the alert radius (fresh prices). Dedup still 1/
  station/hour. 35 tests pass.
This commit is contained in:
FuelBoard Contributor
2026-08-12 14:50:09 +01:00
parent 126fdbde62
commit 3303035c04
+11 -1
View File
@@ -41,6 +41,9 @@ struct TieChoice {
///
/// Region budget: iOS allows 20 monitored regions per app favourites are
/// always registered first, then the closest remaining stations fill the rest.
/// Each region is a circle around a station with radius = the alert radius
/// (clamped to the device ceiling), so the notification fires on APPROACH
/// entering the winner's circle not at the forecourt.
@MainActor
final class ProximityMonitor: NSObject, ObservableObject, @preconcurrency CLLocationManagerDelegate, UNUserNotificationCenterDelegate {
@Published var monitoredStationIDs: [String] = []
@@ -117,10 +120,17 @@ final class ProximityMonitor: NSObject, ObservableObject, @preconcurrency CLLoca
candidates.append(contentsOf: others)
var registered: [String] = []
// Trigger radius = the ALERT radius (approach heads-up), not a tiny
// forecourt fence: entering the winner's region means you're within
// the alert radius of it, and the notification fires as you approach.
// Clamp to the device's region-monitoring ceiling registering a
// larger region fails (kCLErrorRegionMonitoringFailure) or is reduced.
let maxRegion = manager.maximumRegionMonitoringDistance
let triggerRadius = maxRegion > 0 ? min(radiusKM * 1000, maxRegion) : radiusKM * 1000
for station in candidates.prefix(18) where station.prices[fuel] != nil {
let region = CLCircularRegion(
center: CLLocationCoordinate2D(latitude: station.lat, longitude: station.lng),
radius: 300,
radius: triggerRadius,
identifier: station.id
)
region.notifyOnEntry = true