Widget diagnostics: beacon from makeEntry (keychain + relay) + app diagnostics section
The small-widget skeleton survived four resolution fixes; instrument the widget instead of guessing. Every makeEntry now writes its entry state (intent type, location source, station count, fuel, sort, favourites flag, first station) to a keychain beacon (app-readable) and fire-and-forgets a GET to the relay's /api/v1/widget-diag so its access log records whether a widget timeline actually runs in the extension and what it produced. Settings → Debug gains a Widget Diagnostics section: the keychain beacon + the installed-widget inventory from WidgetCenter. Relay gains the /api/v1/widget-diag route (204, log-only).
This commit is contained in:
@@ -127,6 +127,36 @@ struct FuelPriceTimelineProvider<Configuration: WidgetConfigurationIntent & Widg
|
||||
}
|
||||
|
||||
private func makeEntry(configuration: Configuration) async -> FuelPriceEntry {
|
||||
let entry = await makeEntryCore(configuration: configuration)
|
||||
Self.writeDiagBeacon(entry: entry)
|
||||
return entry
|
||||
}
|
||||
|
||||
/// Fire-and-forget diagnostics beacon: writes the entry state to keychain
|
||||
/// (app-readable) and GETs the relay so its access log records that a
|
||||
/// widget timeline actually ran in the extension and what it produced.
|
||||
/// Deliberately outside the timeline result — can never affect rendering.
|
||||
private static func writeDiagBeacon(entry: FuelPriceEntry) {
|
||||
let first = entry.stations.first
|
||||
let json = """
|
||||
{"intent":"\(Configuration.self)","source":"\(entry.locationSource)",\
|
||||
"n":\(entry.stations.count),"fuel":"\(entry.fuel.rawValue)",\
|
||||
"sort":"\(entry.sort.rawValue)","fav":\(entry.isFavourites),\
|
||||
"station":"\(first?.name ?? "")","price":\(first?.prices[entry.fuel] ?? -1)}
|
||||
"""
|
||||
FuelStore.saveWidgetDiag(json)
|
||||
guard let url = URLComponents(
|
||||
url: RelayFuelProvider().baseURL.appendingPathComponent("api/v1/widget-diag"),
|
||||
resolvingAgainstBaseURL: false
|
||||
)?.url else { return }
|
||||
var request = URLRequest(url: url)
|
||||
request.timeoutInterval = 2
|
||||
Task {
|
||||
_ = try? await URLSession.shared.data(for: request)
|
||||
}
|
||||
}
|
||||
|
||||
private func makeEntryCore(configuration: Configuration) async -> FuelPriceEntry {
|
||||
// Per-widget config: fuel + sort + distance come from THIS widget instance.
|
||||
let fuel = FuelType(rawValue: configuration.fuel.rawValue) ?? .e10
|
||||
let isFavourites = configuration.sort == .favourites
|
||||
|
||||
Reference in New Issue
Block a user