Deliver alert immediately; map attaches in place afterwards
The notification used to be added only inside the MKMapSnapshotter completion — if the snapshot hung or failed, no notification was ever delivered and the result line never showed the map status. Now the notification fires immediately (same identifier), the snapshot renders in the background with an 8s timeout, and on success the delivered notification is replaced in place with the map attached. Result line always updates: preparing -> attached / failed / timed out.
This commit is contained in:
@@ -207,6 +207,16 @@ final class ProximityMonitor: NSObject, ObservableObject, @preconcurrency CLLoca
|
||||
"stationLat": station.lat,
|
||||
"stationLng": station.lng,
|
||||
]
|
||||
let identifier = UUID().uuidString
|
||||
|
||||
// Deliver the notification IMMEDIATELY — the alert must never wait on
|
||||
// the map. If the snapshot later succeeds, the same identifier is used
|
||||
// to replace the delivered notification with the map attached.
|
||||
mapStatus?("map: preparing…")
|
||||
UNUserNotificationCenter.current().add(
|
||||
UNNotificationRequest(identifier: identifier, content: content, trigger: nil)
|
||||
)
|
||||
|
||||
let options = MKMapSnapshotter.Options()
|
||||
options.region = MKCoordinateRegion(
|
||||
center: CLLocationCoordinate2D(latitude: station.lat, longitude: station.lng),
|
||||
@@ -215,8 +225,24 @@ final class ProximityMonitor: NSObject, ObservableObject, @preconcurrency CLLoca
|
||||
options.size = CGSize(width: 800, height: 400)
|
||||
options.mapType = .standard
|
||||
let snapshotter = MKMapSnapshotter(options: options)
|
||||
snapshotter.start { [content] snapshot, error in
|
||||
|
||||
// Timeout so a hung snapshot can't leave the result line stuck on
|
||||
// "preparing" forever.
|
||||
var finished = false
|
||||
let timeout = DispatchWorkItem {
|
||||
Task { @MainActor in
|
||||
guard !finished else { return }
|
||||
finished = true
|
||||
mapStatus?("map: timed out")
|
||||
}
|
||||
}
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 8, execute: timeout)
|
||||
|
||||
snapshotter.start { [content] snapshot, error in
|
||||
timeout.cancel()
|
||||
Task { @MainActor in
|
||||
guard !finished else { return }
|
||||
finished = true
|
||||
let finalContent = content
|
||||
var status = "map: no image"
|
||||
if let snapshot, error == nil,
|
||||
@@ -232,8 +258,11 @@ final class ProximityMonitor: NSObject, ObservableObject, @preconcurrency CLLoca
|
||||
status = "map: snapshot failed (\(error.localizedDescription))"
|
||||
}
|
||||
mapStatus?(status)
|
||||
let request = UNNotificationRequest(identifier: UUID().uuidString, content: finalContent, trigger: nil)
|
||||
UNUserNotificationCenter.current().add(request)
|
||||
// Replace the delivered notification (same identifier) so the
|
||||
// map appears in place.
|
||||
UNUserNotificationCenter.current().add(
|
||||
UNNotificationRequest(identifier: identifier, content: finalContent, trigger: nil)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -282,10 +311,10 @@ final class ProximityMonitor: NSObject, ObservableObject, @preconcurrency CLLoca
|
||||
content.body = "\(candidate.name) · \(distanceText) away · \(String(format: "%.1fp", price)). Tap for directions."
|
||||
content.sound = .default
|
||||
let baseResult = "\(fuel.displayName) · \(brand) · \(String(format: "%.1fp", price)) · \(distanceText) away · radius \(unit.format(radiusKM))"
|
||||
lastTestResult = baseResult
|
||||
addAlertRequest(content: content, station: candidate) { status in
|
||||
self.lastTestResult = "\(baseResult) · \(status)"
|
||||
}
|
||||
lastTestResult = baseResult
|
||||
} else {
|
||||
// No real candidate (no stations loaded yet, or none sell the
|
||||
// monitored fuel) — still fire so delivery is testable, but say
|
||||
|
||||
Reference in New Issue
Block a user