From 6375af4c05f02ed78cce1d8772489c6b08f2e579 Mon Sep 17 00:00:00 2001 From: FuelBoard Contributor Date: Fri, 14 Aug 2026 02:28:43 +0100 Subject: [PATCH] Small widget: restore its own intent TYPE (minimal params) for the icon menu MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit iOS keys the long-press app-icon contextual menu by configuration-intent TYPE, so sharing one intent across kinds collapsed the menu to the first-registered kind (small) — the medium 'FuelBoard Prices' vanished from the icon menu while the + gallery (kind-keyed) still listed both. dafc96f's skeleton fix was never the problem; the extra favourite AppEntity params were. FIX: FuelBoardSmallWidgetConfigurationIntent — a distinct intent TYPE with the IDENTICAL minimal parameter set (fuel/sort/distance only, no AppEntity params). Fresh-widget default-config resolution stays intact; the appex metadata now registers two WidgetConfiguration actions. Verified: build green, 45/45 tests, extract.actionsdata lists both intents, fresh IPA packaged + signed per component. --- FuelBoardWidgets/FuelPriceWidget.swift | 20 ++++++---- FuelBoardWidgets/WidgetConfigIntent.swift | 48 +++++++++++++++++++++++ 2 files changed, 60 insertions(+), 8 deletions(-) diff --git a/FuelBoardWidgets/FuelPriceWidget.swift b/FuelBoardWidgets/FuelPriceWidget.swift index 9eee29e..26df698 100644 --- a/FuelBoardWidgets/FuelPriceWidget.swift +++ b/FuelBoardWidgets/FuelPriceWidget.swift @@ -76,20 +76,24 @@ struct FuelPriceWidget: Widget { } // Small widget — exactly ONE station (the first of the configured result). -// Shares the medium widget's intent: the small intent with extra favourite -// AppEntity params failed fresh-widget default-config resolution at the -// system level (whole default config failed → no timeline → skeleton; proven -// by an A/B test where a small widget using this intent populated while the -// custom-intent small widget never even reached makeEntry). Favourites sort -// on the small face shows the cheapest favourite of the chosen fuel. +// Uses its OWN intent TYPE (FuelBoardSmallWidgetConfigurationIntent) with the +// IDENTICAL minimal parameter set as the medium intent. iOS keys the +// long-press app-icon contextual menu by configuration-intent TYPE, so two +// kinds sharing one intent collapsed there to the first-registered kind (the +// small widget won — medium vanished from the icon menu). Distinct intent +// TYPE, same minimal params: restores both entries in the icon menu AND keeps +// fresh-widget default-config resolution working (the skeleton root cause was +// the extra favourite AppEntity params, not intent separation — see the +// Fresh-widget defaults section). Favourites sort on the small face shows the +// cheapest favourite of the chosen fuel. struct FuelPriceSmallWidget: Widget { let kind = "FuelPriceWidgetSmall" var body: some WidgetConfiguration { AppIntentConfiguration( kind: kind, - intent: FuelBoardWidgetConfigurationIntent.self, - provider: FuelPriceTimelineProvider( + intent: FuelBoardSmallWidgetConfigurationIntent.self, + provider: FuelPriceTimelineProvider( beaconTag: "FuelBoardSmallWidget" ) ) { entry in diff --git a/FuelBoardWidgets/WidgetConfigIntent.swift b/FuelBoardWidgets/WidgetConfigIntent.swift index 900fcce..99ba680 100644 --- a/FuelBoardWidgets/WidgetConfigIntent.swift +++ b/FuelBoardWidgets/WidgetConfigIntent.swift @@ -120,3 +120,51 @@ struct FuelBoardWidgetConfigurationIntent: WidgetConfigurationIntent, WidgetConf } } } + +// The SMALL widget's OWN intent TYPE. iOS keys the long-press app-icon +// contextual menu (iOS 18+ "turn icon into a widget") by configuration-intent +// TYPE, so two widget kinds sharing one intent collapse there to a single +// entry — the first-registered kind. (The + gallery is kind-keyed, which is +// why it still lists both.) This intent carries the IDENTICAL minimal +// parameter set as the medium intent — fuel/sort/distance only, NO AppEntity +// params (the favouriteFuel/favourite AppEntity params were what broke +// fresh-widget default-config resolution, not intent separation; see +// Fresh-widget defaults). Sharing the generic provider keeps the timeline +// logic in one place. +struct FuelBoardSmallWidgetConfigurationIntent: WidgetConfigurationIntent, WidgetConfigValues { + static var title: LocalizedStringResource = "Fuel & Sort" + static var description = IntentDescription("Which fuel, ordering and search radius this widget shows.") + + @Parameter(title: "Fuel", default: .e10) + var fuel: WidgetFuel + + @Parameter(title: "Sort by", default: .cheapest) + var sort: WidgetSort + + @Parameter(title: "Distance", default: WidgetDistance(id: 5)) + var distance: WidgetDistance + + /// Edit-Widget UI: same layout as the medium intent — Distance only for + /// Cheapest ordering; hidden for Closest and Favourites. + static var parameterSummary: some ParameterSummary { + When(\.$sort, .equalTo, WidgetSort.cheapest) { + Summary("Show \(\.$fuel) by \(\.$sort) within \(\.$distance)") { + \.$fuel + \.$sort + \.$distance + } + } otherwise: { + When(\.$sort, .equalTo, WidgetSort.favourites) { + Summary("Show \(\.$fuel) favourites") { + \.$fuel + \.$sort + } + } otherwise: { + Summary("Show \(\.$fuel) by \(\.$sort)") { + \.$fuel + \.$sort + } + } + } + } +}