From 3cf1d5c9af3bba3c891386cbc2a93eacda9e4e90 Mon Sep 17 00:00:00 2001 From: FuelBoard Contributor Date: Thu, 13 Aug 2026 20:13:24 +0100 Subject: [PATCH] Widget diagnostics: per-intent beacons (keychain slots + relay query) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The single-slot beacon couldn't distinguish small vs medium (last writer wins). Beacon keychain service now embeds the intent type name so small and medium never overwrite each other; the relay GET now carries the intent type + entry state as query params so the access log shows exactly which widget kind ran makeEntry, when, and with what. Diagnostics screen shows SMALL and MEDIUM beacons side by side — a missing SMALL beacon after adding the small widget proves its timeline never reaches the provider (system-level config resolution failure). --- FuelBoard/SettingsView.swift | 28 ++++++++++++++++++-------- FuelBoardWidgets/FuelPriceWidget.swift | 27 ++++++++++++++++++------- Shared/FuelStore.swift | 18 +++++++++-------- 3 files changed, 50 insertions(+), 23 deletions(-) diff --git a/FuelBoard/SettingsView.swift b/FuelBoard/SettingsView.swift index 7b1d326..aac1800 100644 --- a/FuelBoard/SettingsView.swift +++ b/FuelBoard/SettingsView.swift @@ -45,13 +45,15 @@ struct SettingsView: View { @State private var debugMode: Bool = FuelStore.loadDebugMode() @State private var versionTapCount = 0 @State private var lastVersionTap = Date.distantPast - /// Widget diagnostics: the extension's last makeEntry beacon (keychain) - /// + the installed-widget inventory from WidgetCenter. - @State private var widgetDiagBeacon: String? + /// Widget diagnostics: the extension's last makeEntry beacon PER intent + /// type (keychain) + the installed-widget inventory from WidgetCenter. + @State private var smallWidgetDiag: String? + @State private var mediumWidgetDiag: String? @State private var installedWidgets: String = "" private func refreshWidgetDiag() { - widgetDiagBeacon = FuelStore.loadWidgetDiag() + smallWidgetDiag = FuelStore.loadWidgetDiag(intentType: "FuelBoardSmallWidgetConfigurationIntent") + mediumWidgetDiag = FuelStore.loadWidgetDiag(intentType: "FuelBoardWidgetConfigurationIntent") WidgetCenter.shared.getCurrentConfigurations { result in let text: String switch result { @@ -139,13 +141,23 @@ struct SettingsView: View { } label: { Label("Refresh widget diagnostics", systemImage: "arrow.clockwise") } - if let widgetDiagBeacon { - Text(widgetDiagBeacon) + if let smallWidgetDiag { + Text("SMALL: \(smallWidgetDiag)") .font(.caption2) .foregroundStyle(.secondary) .textSelection(.enabled) } else { - Text("No widget beacon yet — add a widget on the home screen, then refresh.") + Text("SMALL: no beacon yet") + .font(.caption2) + .foregroundStyle(.secondary) + } + if let mediumWidgetDiag { + Text("MEDIUM: \(mediumWidgetDiag)") + .font(.caption2) + .foregroundStyle(.secondary) + .textSelection(.enabled) + } else { + Text("MEDIUM: no beacon yet") .font(.caption2) .foregroundStyle(.secondary) } @@ -155,7 +167,7 @@ struct SettingsView: View { } header: { Text("Widget Diagnostics") } footer: { - Text("The beacon is written by the widget extension at the end of every timeline entry (keychain, so it survives free-SideStore installs). Installed widgets come from WidgetCenter.") + Text("Each beacon is written by the widget extension at the end of every timeline entry for that intent type (keychain, so it survives free-SideStore installs). A missing SMALL beacon after adding the small widget means its timeline never reaches the provider. Installed widgets come from WidgetCenter.") } .onAppear { refreshWidgetDiag() } } diff --git a/FuelBoardWidgets/FuelPriceWidget.swift b/FuelBoardWidgets/FuelPriceWidget.swift index 0c4186c..45088e0 100644 --- a/FuelBoardWidgets/FuelPriceWidget.swift +++ b/FuelBoardWidgets/FuelPriceWidget.swift @@ -133,22 +133,35 @@ struct FuelPriceTimelineProvider String? { - loadString(service: "widget.diag") + static func loadWidgetDiag(intentType: String) -> String? { + loadString(service: "widget.diag.\(intentType)") } }