Live-test fixes: CarPlay disfavor, widget location refresh, debug cheapest parity
1. CarPlay widget taps could never launch Maps — FuelBoard is not a CarPlay app, and Apple only lets a widget launch its own app in CarPlay when that app is CarPlay-enabled. Marked the widget as a disfavored CarPlay location (iOS 26+; no-op below): read-only in the car, no dead tap. Header comment updated to match reality. 2. Widgets now track the user's location: timeline refresh 15m -> 5m, and the app's location manager reloads all widget timelines after every significant move (250m filter, throttled to 60s), so the widget re-orders around the new position while driving instead of waiting for the next tick. 3. Debug 'Cheapest in range' no longer shows the ALERT prediction (alertsFuel + alert radius — a different pool by design). It now mirrors the app list exactly: the TOP-badge station for the selected fuel within the stationLimit radius (new DebugAppCheapest computed in ContentView, passed to Settings). Alert-prediction cheapest kept as fallback when the app view hasn't resolved.
This commit is contained in:
@@ -5,13 +5,18 @@ import AppIntents
|
||||
// FuelBoard widget — petrol stations near you.
|
||||
// systemMedium: top 3-4 stations with price + distance, each row opens Maps
|
||||
// systemSmall: single station, whole widget opens Maps
|
||||
// Taps deep-link to Apple Maps directions (maps://?daddr=). The widget uses
|
||||
// the native Maps scheme (not a custom app scheme) so that CarPlay can route
|
||||
// the tap straight to Apple Maps — which IS a CarPlay app — without needing
|
||||
// the containing app to launch. On the Home Screen the system either opens
|
||||
// Maps directly or delivers the URL to FuelBoard, whose onOpenURL forwards it
|
||||
// (and also still handles legacy fuelboard:// and http://maps.apple.com links
|
||||
// from older cached timelines).
|
||||
// Taps deep-link to Apple Maps directions (maps://?daddr=). On the Home
|
||||
// Screen the system either opens Maps directly or delivers the URL to
|
||||
// FuelBoard, whose onOpenURL forwards it (and also still handles legacy
|
||||
// fuelboard:// and http://maps.apple.com links from older cached timelines).
|
||||
//
|
||||
// CarPlay: this widget is marked as a DISFAVORED location there. Widgets in
|
||||
// CarPlay can only launch their OWN app, and only when that app is itself a
|
||||
// CarPlay app (fueling entitlement). FuelBoard is not a CarPlay app, so a
|
||||
// tap in the car would be a dead interaction — Apple's guidance for widgets
|
||||
// whose purpose is launching a non-CarPlay app is to disfavor CarPlay: the
|
||||
// widget stays visible (read-only prices) but interaction is disabled, so
|
||||
// there is no tap that silently does nothing.
|
||||
//
|
||||
// Each widget instance is configured INDEPENDENTLY via its own App Intent
|
||||
// (long-press → Edit Widget): fuel type (Unleaded/Premium/Diesel) and sort
|
||||
@@ -30,6 +35,24 @@ struct FuelPriceWidget: Widget {
|
||||
let kind = "FuelPriceWidget"
|
||||
|
||||
var body: some WidgetConfiguration {
|
||||
// CarPlay: mark as disfavored — FuelBoard is not a CarPlay app, so
|
||||
// widget taps there can never launch Maps (Apple only allows a widget
|
||||
// to launch its own app in CarPlay, and only CarPlay-enabled apps).
|
||||
// Disfavored = read-only in the car, no dead interaction. The
|
||||
// locations array is empty below iOS 26 where `.carPlay` doesn't
|
||||
// exist, making the modifier a harmless no-op there.
|
||||
baseConfiguration()
|
||||
.disfavoredLocations(carPlayLocations, for: [.systemSmall])
|
||||
}
|
||||
|
||||
private var carPlayLocations: [WidgetLocation] {
|
||||
if #available(iOS 26.0, *) {
|
||||
return [.carPlay]
|
||||
}
|
||||
return []
|
||||
}
|
||||
|
||||
private func baseConfiguration() -> some WidgetConfiguration {
|
||||
AppIntentConfiguration(
|
||||
kind: kind,
|
||||
intent: FuelBoardWidgetConfigurationIntent.self,
|
||||
@@ -79,7 +102,9 @@ struct FuelPriceTimelineProvider: AppIntentTimelineProvider {
|
||||
in context: Context
|
||||
) async -> Timeline<FuelPriceEntry> {
|
||||
let entry = await makeEntry(configuration: configuration)
|
||||
let nextRefresh = Calendar.current.date(byAdding: .minute, value: 15, to: Date())!
|
||||
// Short cadence so the widget re-orders around a new location while
|
||||
// driving — the provider re-fetches a fresh fix + prices each tick.
|
||||
let nextRefresh = Calendar.current.date(byAdding: .minute, value: 5, to: Date())!
|
||||
return Timeline(entries: [entry], policy: .after(nextRefresh))
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user