Small widget: restore its own intent TYPE (minimal params) for the icon menu

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.
This commit is contained in:
FuelBoard Contributor
2026-08-14 02:28:43 +01:00
parent 050c9f13a3
commit 6375af4c05
2 changed files with 60 additions and 8 deletions
+12 -8
View File
@@ -76,20 +76,24 @@ struct FuelPriceWidget: Widget {
} }
// Small widget exactly ONE station (the first of the configured result). // Small widget exactly ONE station (the first of the configured result).
// Shares the medium widget's intent: the small intent with extra favourite // Uses its OWN intent TYPE (FuelBoardSmallWidgetConfigurationIntent) with the
// AppEntity params failed fresh-widget default-config resolution at the // IDENTICAL minimal parameter set as the medium intent. iOS keys the
// system level (whole default config failed no timeline skeleton; proven // long-press app-icon contextual menu by configuration-intent TYPE, so two
// by an A/B test where a small widget using this intent populated while the // kinds sharing one intent collapsed there to the first-registered kind (the
// custom-intent small widget never even reached makeEntry). Favourites sort // small widget won medium vanished from the icon menu). Distinct intent
// on the small face shows the cheapest favourite of the chosen fuel. // 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 { struct FuelPriceSmallWidget: Widget {
let kind = "FuelPriceWidgetSmall" let kind = "FuelPriceWidgetSmall"
var body: some WidgetConfiguration { var body: some WidgetConfiguration {
AppIntentConfiguration( AppIntentConfiguration(
kind: kind, kind: kind,
intent: FuelBoardWidgetConfigurationIntent.self, intent: FuelBoardSmallWidgetConfigurationIntent.self,
provider: FuelPriceTimelineProvider<FuelBoardWidgetConfigurationIntent>( provider: FuelPriceTimelineProvider<FuelBoardSmallWidgetConfigurationIntent>(
beaconTag: "FuelBoardSmallWidget" beaconTag: "FuelBoardSmallWidget"
) )
) { entry in ) { entry in
+48
View File
@@ -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
}
}
}
}
}