Favourites are fuel-scoped: starring pins the station for one fuel only
Fixing the bug where favouriting an Unleaded entry also created a Diesel favourite. Favourites are now [FavouriteEntry] = (station, fuel) pairs: - Starring a row pins it only for the fuel being viewed - Favourites tabs show only fuels that actually have favourites - Geofence monitor gives priority slots only to favourites matching the monitored fuel - Tests updated + new fuel-scoping test (31/31 passing)
This commit is contained in:
@@ -10,7 +10,7 @@ struct ContentView: View {
|
|||||||
@State private var sortMode: SortMode = FuelStore.loadSortMode()
|
@State private var sortMode: SortMode = FuelStore.loadSortMode()
|
||||||
@State private var stationLimit: Int = FuelStore.loadStationLimit()
|
@State private var stationLimit: Int = FuelStore.loadStationLimit()
|
||||||
@State private var distanceUnit: DistanceUnit = FuelStore.loadDistanceUnit()
|
@State private var distanceUnit: DistanceUnit = FuelStore.loadDistanceUnit()
|
||||||
@State private var favourites: [FuelStation] = FuelStore.loadFavourites()
|
@State private var favourites: [FavouriteEntry] = FuelStore.loadFavourites()
|
||||||
@State private var alertsEnabled: Bool = FuelStore.loadAlertsEnabled()
|
@State private var alertsEnabled: Bool = FuelStore.loadAlertsEnabled()
|
||||||
@State private var alertsRadius: Double = FuelStore.loadAlertsRadius()
|
@State private var alertsRadius: Double = FuelStore.loadAlertsRadius()
|
||||||
@State private var location: Coordinate? = {
|
@State private var location: Coordinate? = {
|
||||||
@@ -95,11 +95,13 @@ struct ContentView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Station IDs favourited for the CURRENTLY selected fuel — the star on a
|
||||||
|
/// Stations-tab row reflects the fuel being viewed (fuel-scoped favourites).
|
||||||
private var favouriteIDs: Set<String> {
|
private var favouriteIDs: Set<String> {
|
||||||
Set(favourites.map(\.id))
|
Set(favourites.filter { $0.fuel == selectedFuel }.map(\.station.id))
|
||||||
}
|
}
|
||||||
|
|
||||||
private var refreshedFavourites: [FuelStation] {
|
private var refreshedFavourites: [FavouriteEntry] {
|
||||||
FuelStore.refreshedFavourites(favourites, from: stations)
|
FuelStore.refreshedFavourites(favourites, from: stations)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -127,7 +129,6 @@ struct ContentView: View {
|
|||||||
selectedFuel: selectedFuel,
|
selectedFuel: selectedFuel,
|
||||||
location: location,
|
location: location,
|
||||||
distanceUnit: distanceUnit,
|
distanceUnit: distanceUnit,
|
||||||
favouriteIDs: favouriteIDs,
|
|
||||||
onToggleFavourite: toggleFavourite
|
onToggleFavourite: toggleFavourite
|
||||||
)
|
)
|
||||||
.tabItem { Label("Favourites", systemImage: "star.fill") }
|
.tabItem { Label("Favourites", systemImage: "star.fill") }
|
||||||
@@ -222,11 +223,12 @@ struct ContentView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func toggleFavourite(_ station: FuelStation) {
|
private func toggleFavourite(_ station: FuelStation, fuel: FuelType) {
|
||||||
if favouriteIDs.contains(station.id) {
|
let key = FavouriteEntry(station: station, fuel: fuel)
|
||||||
favourites.removeAll { $0.id == station.id }
|
if favourites.contains(where: { $0.id == key.id }) {
|
||||||
|
favourites.removeAll { $0.id == key.id }
|
||||||
} else {
|
} else {
|
||||||
favourites.append(station)
|
favourites.append(key)
|
||||||
}
|
}
|
||||||
FuelStore.saveFavourites(favourites)
|
FuelStore.saveFavourites(favourites)
|
||||||
WidgetCenter.shared.reloadAllTimelines()
|
WidgetCenter.shared.reloadAllTimelines()
|
||||||
|
|||||||
@@ -3,20 +3,21 @@ import SwiftUI
|
|||||||
/// Favourites tab — starred stations, split by fuel type. Each fuel type
|
/// Favourites tab — starred stations, split by fuel type. Each fuel type
|
||||||
/// with at least one favourite appears as its own tab; that tab's favourites
|
/// with at least one favourite appears as its own tab; that tab's favourites
|
||||||
/// are ranked cheapest-first for that fuel, with the cheapest called out.
|
/// are ranked cheapest-first for that fuel, with the cheapest called out.
|
||||||
|
/// Favourites are fuel-scoped entries — starring a station only pins it for
|
||||||
|
/// the fuel you were viewing, so an Unleaded favourite never shows as Diesel.
|
||||||
struct FavouritesView: View {
|
struct FavouritesView: View {
|
||||||
let favourites: [FuelStation]
|
let favourites: [FavouriteEntry]
|
||||||
/// The app's currently selected fuel — used only as the initial tab.
|
/// The app's currently selected fuel — used only as the initial tab.
|
||||||
let selectedFuel: FuelType
|
let selectedFuel: FuelType
|
||||||
let location: Coordinate?
|
let location: Coordinate?
|
||||||
let distanceUnit: DistanceUnit
|
let distanceUnit: DistanceUnit
|
||||||
let favouriteIDs: Set<String>
|
var onToggleFavourite: (FuelStation, FuelType) -> Void = { _, _ in }
|
||||||
var onToggleFavourite: (FuelStation) -> Void = { _ in }
|
|
||||||
|
|
||||||
/// Fuel types that currently have at least one favourite selling them —
|
/// Fuel types that currently have at least one favourite — these are the
|
||||||
/// these are the only tabs shown.
|
/// only tabs shown (a fuel with no favourites gets no tab).
|
||||||
private var availableFuels: [FuelType] {
|
private var availableFuels: [FuelType] {
|
||||||
FuelType.allCases.filter { fuel in
|
FuelType.allCases.filter { fuel in
|
||||||
favourites.contains { $0.prices[fuel] != nil }
|
favourites.contains { $0.fuel == fuel }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -28,9 +29,9 @@ struct FavouritesView: View {
|
|||||||
availableFuels.contains(fuel) ? fuel : (availableFuels.first ?? .e10)
|
availableFuels.contains(fuel) ? fuel : (availableFuels.first ?? .e10)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Favourites that sell the active fuel, cheapest first (distance tiebreak).
|
/// Stations favourited for the active fuel, cheapest first (distance tiebreak).
|
||||||
private var ranked: [FuelStation] {
|
private var ranked: [FuelStation] {
|
||||||
let available = favourites.filter { $0.prices[activeFuel] != nil }
|
let available = favourites.filter { $0.fuel == activeFuel }.map(\.station)
|
||||||
return available.sorted { lhs, rhs in
|
return available.sorted { lhs, rhs in
|
||||||
let lPrice = lhs.prices[activeFuel]!
|
let lPrice = lhs.prices[activeFuel]!
|
||||||
let rPrice = rhs.prices[activeFuel]!
|
let rPrice = rhs.prices[activeFuel]!
|
||||||
@@ -41,20 +42,23 @@ struct FavouritesView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// IDs favourited for the active fuel — the star state on each row.
|
||||||
|
private var activeFuelFavouriteIDs: Set<String> {
|
||||||
|
Set(favourites.filter { $0.fuel == activeFuel }.map(\.station.id))
|
||||||
|
}
|
||||||
|
|
||||||
private var cheapest: FuelStation? { ranked.first }
|
private var cheapest: FuelStation? { ranked.first }
|
||||||
private var cheapestPrice: Double? { cheapest?.prices[activeFuel] }
|
private var cheapestPrice: Double? { cheapest?.prices[activeFuel] }
|
||||||
|
|
||||||
init(favourites: [FuelStation],
|
init(favourites: [FavouriteEntry],
|
||||||
selectedFuel: FuelType,
|
selectedFuel: FuelType,
|
||||||
location: Coordinate?,
|
location: Coordinate?,
|
||||||
distanceUnit: DistanceUnit,
|
distanceUnit: DistanceUnit,
|
||||||
favouriteIDs: Set<String>,
|
onToggleFavourite: @escaping (FuelStation, FuelType) -> Void = { _, _ in }) {
|
||||||
onToggleFavourite: @escaping (FuelStation) -> Void = { _ in }) {
|
|
||||||
self.favourites = favourites
|
self.favourites = favourites
|
||||||
self.selectedFuel = selectedFuel
|
self.selectedFuel = selectedFuel
|
||||||
self.location = location
|
self.location = location
|
||||||
self.distanceUnit = distanceUnit
|
self.distanceUnit = distanceUnit
|
||||||
self.favouriteIDs = favouriteIDs
|
|
||||||
self.onToggleFavourite = onToggleFavourite
|
self.onToggleFavourite = onToggleFavourite
|
||||||
_fuel = State(initialValue: selectedFuel)
|
_fuel = State(initialValue: selectedFuel)
|
||||||
}
|
}
|
||||||
@@ -70,7 +74,7 @@ struct FavouritesView: View {
|
|||||||
.foregroundStyle(.secondary)
|
.foregroundStyle(.secondary)
|
||||||
Text("No favourites yet")
|
Text("No favourites yet")
|
||||||
.font(.headline)
|
.font(.headline)
|
||||||
Text("Tap the star on any station in the Stations tab to pin it here, ranked by which is cheapest.")
|
Text("Tap the star on any station in the Stations tab to pin it here, ranked by which is cheapest for that fuel.")
|
||||||
.font(.caption)
|
.font(.caption)
|
||||||
.foregroundStyle(.secondary)
|
.foregroundStyle(.secondary)
|
||||||
.multilineTextAlignment(.center)
|
.multilineTextAlignment(.center)
|
||||||
@@ -108,8 +112,8 @@ struct FavouritesView: View {
|
|||||||
distanceUnit: distanceUnit,
|
distanceUnit: distanceUnit,
|
||||||
baselinePrice: cheapestPrice,
|
baselinePrice: cheapestPrice,
|
||||||
isTopResult: index == 0,
|
isTopResult: index == 0,
|
||||||
isFavourite: favouriteIDs.contains(station.id),
|
isFavourite: activeFuelFavouriteIDs.contains(station.id),
|
||||||
onToggleFavourite: { onToggleFavourite(station) }
|
onToggleFavourite: { onToggleFavourite(station, activeFuel) }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,15 +31,20 @@ final class ProximityMonitor: NSObject, ObservableObject, @preconcurrency CLLoca
|
|||||||
fuel = FuelStore.loadSelectedFuel()
|
fuel = FuelStore.loadSelectedFuel()
|
||||||
radiusKM = FuelStore.loadAlertsRadius()
|
radiusKM = FuelStore.loadAlertsRadius()
|
||||||
stations = FuelStore.loadStations()
|
stations = FuelStore.loadStations()
|
||||||
favourites = FuelStore.loadFavourites()
|
favourites = FuelStore.loadFavourites().filter { $0.fuel == fuel }.map(\.station)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Re-registers geofences. Call whenever stations/favourites/settings change.
|
/// Re-registers geofences. Call whenever stations/favourites/settings change.
|
||||||
func update(stations: [FuelStation], favourites: [FuelStation], fuel: FuelType, radiusKM: Double) {
|
/// Favourites are fuel-scoped entries; only entries for the monitored fuel
|
||||||
|
/// get priority slots (a Diesel favourite must not grab a slot while
|
||||||
|
/// monitoring Unleaded).
|
||||||
|
func update(stations: [FuelStation], favourites: [FavouriteEntry], fuel: FuelType, radiusKM: Double) {
|
||||||
// Fall back to the shared cache when called before the first fetch
|
// Fall back to the shared cache when called before the first fetch
|
||||||
// completes (launch, background region-event wake).
|
// completes (launch, background region-event wake).
|
||||||
self.stations = stations.isEmpty ? FuelStore.loadStations() : stations
|
self.stations = stations.isEmpty ? FuelStore.loadStations() : stations
|
||||||
self.favourites = favourites.isEmpty ? FuelStore.loadFavourites() : favourites
|
self.favourites = favourites.isEmpty
|
||||||
|
? FuelStore.loadFavourites().filter { $0.fuel == fuel }.map(\.station)
|
||||||
|
: favourites.filter { $0.fuel == fuel }.map(\.station)
|
||||||
self.fuel = fuel
|
self.fuel = fuel
|
||||||
self.radiusKM = radiusKM
|
self.radiusKM = radiusKM
|
||||||
|
|
||||||
@@ -51,8 +56,9 @@ final class ProximityMonitor: NSObject, ObservableObject, @preconcurrency CLLoca
|
|||||||
guard enabled else { return }
|
guard enabled else { return }
|
||||||
|
|
||||||
// Favourites first (guaranteed slots), then closest stations, max 18.
|
// Favourites first (guaranteed slots), then closest stations, max 18.
|
||||||
var candidates: [FuelStation] = favourites
|
// `self.favourites` is already filtered to the monitored fuel.
|
||||||
let favIDs = Set(favourites.map(\.id))
|
var candidates: [FuelStation] = self.favourites
|
||||||
|
let favIDs = Set(self.favourites.map(\.id))
|
||||||
let location = FuelStore.loadLocation()
|
let location = FuelStore.loadLocation()
|
||||||
let others = stations
|
let others = stations
|
||||||
.filter { !favIDs.contains($0.id) }
|
.filter { !favIDs.contains($0.id) }
|
||||||
@@ -88,7 +94,7 @@ final class ProximityMonitor: NSObject, ObservableObject, @preconcurrency CLLoca
|
|||||||
} else {
|
} else {
|
||||||
requestPermissions()
|
requestPermissions()
|
||||||
// Re-register geofences from restored cache immediately.
|
// Re-register geofences from restored cache immediately.
|
||||||
update(stations: stations, favourites: favourites, fuel: fuel, radiusKM: radiusKM)
|
update(stations: stations, favourites: FuelStore.loadFavourites(), fuel: fuel, radiusKM: radiusKM)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ struct StationsView: View {
|
|||||||
let topStationID: String?
|
let topStationID: String?
|
||||||
let location: Coordinate?
|
let location: Coordinate?
|
||||||
let favouriteIDs: Set<String>
|
let favouriteIDs: Set<String>
|
||||||
var onToggleFavourite: (FuelStation) -> Void = { _ in }
|
var onToggleFavourite: (FuelStation, FuelType) -> Void = { _, _ in }
|
||||||
var onRefresh: () async -> Void = {}
|
var onRefresh: () async -> Void = {}
|
||||||
|
|
||||||
/// Pagination: one page = 10 rows, reset whenever the underlying list
|
/// Pagination: one page = 10 rows, reset whenever the underlying list
|
||||||
@@ -107,7 +107,7 @@ struct StationsView: View {
|
|||||||
baselinePrice: baselinePrice,
|
baselinePrice: baselinePrice,
|
||||||
isTopResult: station.id == topStationID,
|
isTopResult: station.id == topStationID,
|
||||||
isFavourite: favouriteIDs.contains(station.id),
|
isFavourite: favouriteIDs.contains(station.id),
|
||||||
onToggleFavourite: { onToggleFavourite(station) }
|
onToggleFavourite: { onToggleFavourite(station, selectedFuel) }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -217,18 +217,29 @@ final class FuelTypeLabelTests: XCTestCase {
|
|||||||
|
|
||||||
final class FavouriteRefreshTests: XCTestCase {
|
final class FavouriteRefreshTests: XCTestCase {
|
||||||
func testRefreshedFavouritesApplyFreshPrices() {
|
func testRefreshedFavouritesApplyFreshPrices() {
|
||||||
let fav = FuelStation(id: "s1", name: "OLD NAME", brand: "X", address: "", postcode: "", lat: 0, lng: 0, prices: [.e10: 140.0], priceUpdated: nil)
|
let fav = FavouriteEntry(station: FuelStation(id: "s1", name: "OLD NAME", brand: "X", address: "", postcode: "", lat: 0, lng: 0, prices: [.e10: 140.0], priceUpdated: nil), fuel: .e10)
|
||||||
let fresh = FuelStation(id: "s1", name: "Fresh Station", brand: "X", address: "", postcode: "", lat: 0, lng: 0, prices: [.e10: 132.9], priceUpdated: nil)
|
let fresh = FuelStation(id: "s1", name: "Fresh Station", brand: "X", address: "", postcode: "", lat: 0, lng: 0, prices: [.e10: 132.9], priceUpdated: nil)
|
||||||
let updated = FuelStore.refreshedFavourites([fav], from: [fresh])
|
let updated = FuelStore.refreshedFavourites([fav], from: [fresh])
|
||||||
XCTAssertEqual(updated.count, 1)
|
XCTAssertEqual(updated.count, 1)
|
||||||
XCTAssertEqual(updated[0].name, "Fresh Station")
|
XCTAssertEqual(updated[0].station.name, "Fresh Station")
|
||||||
XCTAssertEqual(updated[0].prices[.e10], 132.9)
|
XCTAssertEqual(updated[0].station.prices[.e10], 132.9)
|
||||||
|
XCTAssertEqual(updated[0].fuel, .e10, "fuel scoping survives refresh")
|
||||||
}
|
}
|
||||||
|
|
||||||
func testRefreshedFavouritesKeepUnmatchedSnapshot() {
|
func testRefreshedFavouritesKeepUnmatchedSnapshot() {
|
||||||
let fav = FuelStation(id: "s1", name: "Cached", brand: "X", address: "", postcode: "", lat: 0, lng: 0, prices: [.e10: 140.0], priceUpdated: nil)
|
let fav = FavouriteEntry(station: FuelStation(id: "s1", name: "Cached", brand: "X", address: "", postcode: "", lat: 0, lng: 0, prices: [.e10: 140.0], priceUpdated: nil), fuel: .diesel)
|
||||||
let updated = FuelStore.refreshedFavourites([fav], from: [])
|
let updated = FuelStore.refreshedFavourites([fav], from: [])
|
||||||
XCTAssertEqual(updated[0].name, "Cached", "unmatched favourite keeps its snapshot")
|
XCTAssertEqual(updated[0].station.name, "Cached", "unmatched favourite keeps its snapshot")
|
||||||
XCTAssertEqual(updated[0].prices[.e10], 140.0)
|
XCTAssertEqual(updated[0].station.prices[.e10], 140.0)
|
||||||
|
XCTAssertEqual(updated[0].fuel, .diesel, "fuel scoping survives unmatched refresh")
|
||||||
|
}
|
||||||
|
|
||||||
|
func testFavouriteEntryIDIsFuelScoped() {
|
||||||
|
let station = FuelStation(id: "s1", name: "X", brand: "X", address: "", postcode: "", lat: 0, lng: 0, prices: [.e10: 140.0, .diesel: 150.0], priceUpdated: nil)
|
||||||
|
let unleaded = FavouriteEntry(station: station, fuel: .e10)
|
||||||
|
let diesel = FavouriteEntry(station: station, fuel: .diesel)
|
||||||
|
XCTAssertNotEqual(unleaded.id, diesel.id, "same station favourited for two fuels is two distinct favourites")
|
||||||
|
XCTAssertTrue(unleaded.id.hasSuffix("|s1"))
|
||||||
|
XCTAssertTrue(diesel.id.hasPrefix("diesel|"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+28
-17
@@ -17,6 +17,16 @@ struct Coordinate: Equatable, Codable {
|
|||||||
let lng: Double
|
let lng: Double
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A favourite = a station pinned for ONE fuel type. Starring a row while
|
||||||
|
/// viewing Unleaded only creates an Unleaded favourite, so the same station
|
||||||
|
/// can be favourite for Diesel independently (or not at all).
|
||||||
|
struct FavouriteEntry: Identifiable, Codable, Equatable {
|
||||||
|
var station: FuelStation
|
||||||
|
let fuel: FuelType
|
||||||
|
|
||||||
|
var id: String { "\(fuel.rawValue)|\(station.id)" }
|
||||||
|
}
|
||||||
|
|
||||||
enum SortMode: String, Codable, CaseIterable, Identifiable {
|
enum SortMode: String, Codable, CaseIterable, Identifiable {
|
||||||
case cheapest
|
case cheapest
|
||||||
case closest
|
case closest
|
||||||
@@ -234,7 +244,7 @@ struct FuelStore {
|
|||||||
static let sortModeKey = "fuelboard.sortMode" // SortMode raw value
|
static let sortModeKey = "fuelboard.sortMode" // SortMode raw value
|
||||||
static let stationLimitKey = "fuelboard.stationLimitMiles" // Int miles (5/10/15)
|
static let stationLimitKey = "fuelboard.stationLimitMiles" // Int miles (5/10/15)
|
||||||
static let distanceUnitKey = "fuelboard.distanceUnit" // DistanceUnit raw value
|
static let distanceUnitKey = "fuelboard.distanceUnit" // DistanceUnit raw value
|
||||||
static let favouritesKey = "fuelboard.favourites" // [FuelStation] JSON
|
static let favouritesKey = "fuelboard.favourites" // [FavouriteEntry] JSON
|
||||||
static let alertsEnabledKey = "fuelboard.alertsEnabled" // Bool
|
static let alertsEnabledKey = "fuelboard.alertsEnabled" // Bool
|
||||||
static let alertsRadiusKey = "fuelboard.alertsRadius" // Double km
|
static let alertsRadiusKey = "fuelboard.alertsRadius" // Double km
|
||||||
static let onboardingCompletedKey = "fuelboard.onboardingCompleted" // Bool
|
static let onboardingCompletedKey = "fuelboard.onboardingCompleted" // Bool
|
||||||
@@ -348,24 +358,27 @@ struct FuelStore {
|
|||||||
|
|
||||||
// MARK: Favourites
|
// MARK: Favourites
|
||||||
|
|
||||||
static func loadFavourites() -> [FuelStation] {
|
/// A favourite pins a station FOR ONE fuel type. Starring a row while
|
||||||
|
/// viewing Unleaded only creates an Unleaded favourite — the station is
|
||||||
|
/// not automatically favourited for Premium or Diesel.
|
||||||
|
static func loadFavourites() -> [FavouriteEntry] {
|
||||||
if let data = keychainData(service: favouritesKey),
|
if let data = keychainData(service: favouritesKey),
|
||||||
let favs = try? JSONDecoder().decode([FuelStation].self, from: data) {
|
let entries = try? JSONDecoder().decode([FavouriteEntry].self, from: data) {
|
||||||
return favs.map { fav in
|
return entries.map { entry in
|
||||||
var f = fav
|
var e = entry
|
||||||
f.name = fav.name.sanitizedStationTitle
|
e.station.name = entry.station.name.sanitizedStationTitle
|
||||||
return f
|
return e
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let defaults = UserDefaults(suiteName: appGroupSuite),
|
if let defaults = UserDefaults(suiteName: appGroupSuite),
|
||||||
let data = defaults.data(forKey: favouritesKey),
|
let data = defaults.data(forKey: favouritesKey),
|
||||||
let favs = try? JSONDecoder().decode([FuelStation].self, from: data) {
|
let entries = try? JSONDecoder().decode([FavouriteEntry].self, from: data) {
|
||||||
return favs
|
return entries
|
||||||
}
|
}
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
static func saveFavourites(_ favourites: [FuelStation]) {
|
static func saveFavourites(_ favourites: [FavouriteEntry]) {
|
||||||
if let data = try? JSONEncoder().encode(favourites) {
|
if let data = try? JSONEncoder().encode(favourites) {
|
||||||
UserDefaults(suiteName: appGroupSuite)?.set(data, forKey: favouritesKey)
|
UserDefaults(suiteName: appGroupSuite)?.set(data, forKey: favouritesKey)
|
||||||
writeKeychain(data: data, service: favouritesKey)
|
writeKeychain(data: data, service: favouritesKey)
|
||||||
@@ -374,14 +387,12 @@ struct FuelStore {
|
|||||||
|
|
||||||
/// Returns favourites with fresh prices applied from the given station list
|
/// Returns favourites with fresh prices applied from the given station list
|
||||||
/// (favourites keep their cached snapshot when not in the current results).
|
/// (favourites keep their cached snapshot when not in the current results).
|
||||||
static func refreshedFavourites(_ favourites: [FuelStation], from stations: [FuelStation]) -> [FuelStation] {
|
/// The fuel scoping is preserved per entry.
|
||||||
var updated = favourites
|
static func refreshedFavourites(_ favourites: [FavouriteEntry], from stations: [FuelStation]) -> [FavouriteEntry] {
|
||||||
for (i, fav) in favourites.enumerated() {
|
favourites.map { entry in
|
||||||
if let fresh = stations.first(where: { $0.id == fav.id }) {
|
guard let fresh = stations.first(where: { $0.id == entry.station.id }) else { return entry }
|
||||||
updated[i] = fresh
|
return FavouriteEntry(station: fresh, fuel: entry.fuel)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return updated
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Alerts
|
// MARK: Alerts
|
||||||
|
|||||||
Reference in New Issue
Block a user