Widget config sheet: revert WidgetSort to String-backed AppEnum. parameterSummary When(\.$sort, .equalTo, …) silently falls back to the otherwise branch for AppEntity parameters (iOS 17+ bug FB13263902), which hid the Favourite/Favourite Fuel rows on the small widget — the metadata compared against a literal 'WidgetSort.favourites' string instead of the enum case. String AppEnums match reliably (the d8506a0 form). Tradeoff: the Sort picker always offers Favourites; picking it with no favourites shows the empty state. FavouriteFuel entity + IntentParameterDependency filtering unchanged.

This commit is contained in:
FuelBoard Contributor
2026-08-13 17:48:33 +01:00
parent d0bd4e7a8e
commit 3e2d3775f2
+12 -36
View File
@@ -21,48 +21,24 @@ enum WidgetFuel: String, AppEnum, CaseIterable, Codable {
] ]
} }
// Per-widget sort order. Modelled as an AppEntity (not an AppEnum) so the // Per-widget sort order. A String-backed AppEnum (NOT an AppEntity): the
// options can be dynamic: "Favourites" only appears when the user actually // parameterSummary conditionals (When(\.$sort, .equalTo, )) fall back to the
// has favourites an empty favourite list makes the option dead weight. // otherwise branch for AppEntity parameters (iOS 17+ bug FB13263902), which
struct WidgetSort: AppEntity, Identifiable, Hashable, Codable { // hid the Favourite picker. String AppEnums match reliably. Tradeoff: the
enum Kind: String, Codable { // option set is static "Favourites" always appears, and picking it with no
// favourites shows the empty state.
enum WidgetSort: String, AppEnum, CaseIterable, Codable {
case cheapest case cheapest
case closest case closest
case favourites case favourites
}
let id: String
var kind: Kind { Kind(rawValue: id) ?? .cheapest }
static let cheapest = WidgetSort(id: "cheapest")
static let closest = WidgetSort(id: "closest")
static let favourites = WidgetSort(id: "favourites")
var displayRepresentation: DisplayRepresentation {
switch kind {
case .cheapest: return DisplayRepresentation(stringLiteral: "Cheapest")
case .closest: return DisplayRepresentation(stringLiteral: "Closest")
case .favourites: return DisplayRepresentation(stringLiteral: "Favourites")
}
}
static var typeDisplayRepresentation: TypeDisplayRepresentation = "Sort by" static var typeDisplayRepresentation: TypeDisplayRepresentation = "Sort by"
static var defaultQuery = WidgetSortQuery()
}
struct WidgetSortQuery: EntityQuery { static var caseDisplayRepresentations: [WidgetSort: DisplayRepresentation] = [
func entities(for identifiers: [String]) async throws -> [WidgetSort] { .cheapest: "Cheapest",
identifiers.map { WidgetSort(id: $0) } .closest: "Closest",
} .favourites: "Favourites",
]
func suggestedEntities() async throws -> [WidgetSort] {
var sorts = [WidgetSort.cheapest, WidgetSort.closest]
if !FuelStore.loadFavourites().isEmpty {
sorts.append(.favourites)
}
return sorts
}
} }
// Per-widget distance filter. Modelled as an AppEntity (not an AppEnum) so // Per-widget distance filter. Modelled as an AppEntity (not an AppEnum) so