England-wide: relay-backed provider (full-UK data), regional sample fallback replaces Halifax-only
This commit is contained in:
+121
-35
@@ -18,14 +18,71 @@ protocol FuelPriceProviding {
|
||||
}
|
||||
|
||||
enum FuelPriceProvider {
|
||||
static let active: FuelPriceProviding = SampleFuelProvider()
|
||||
/// Default: the keyless relay (full-UK Fuel Finder data). Falls back to
|
||||
/// the England-wide sample set when the relay is unreachable.
|
||||
static let active: FuelPriceProviding = RelayFuelProvider()
|
||||
}
|
||||
|
||||
// MARK: - Sample provider (default for the scaffold)
|
||||
// MARK: - Relay provider (default)
|
||||
|
||||
/// Ships realistic stations around Halifax, West Yorkshire (53.7270, -1.8575)
|
||||
/// so the app and widget render meaningful distances for the user with zero
|
||||
/// setup. Prices in pence/litre.
|
||||
/// Talks to the FuelBoard Relay (keyless proxy). The relay serves the FULL-UK
|
||||
/// Fuel Finder dataset — every station in England, Wales, Scotland, NI — and
|
||||
/// filters/sorts server-side. No credentials in the app.
|
||||
struct RelayFuelProvider: FuelPriceProviding {
|
||||
var baseURL = URL(string: "http://192.168.1.131:8788")!
|
||||
|
||||
func fetchStations(near lat: Double?, lng: Double?, fuel: FuelType) async throws -> [FuelStation] {
|
||||
var components = URLComponents(url: baseURL.appendingPathComponent("api/v1/stations"), resolvingAgainstBaseURL: false)!
|
||||
var query: [URLQueryItem] = [URLQueryItem(name: "fuel", value: fuel.rawValue)]
|
||||
if let lat, let lng {
|
||||
query.append(URLQueryItem(name: "lat", value: String(lat)))
|
||||
query.append(URLQueryItem(name: "lng", value: String(lng)))
|
||||
query.append(URLQueryItem(name: "radius", value: "50"))
|
||||
}
|
||||
query.append(URLQueryItem(name: "limit", value: "100"))
|
||||
components.queryItems = query
|
||||
|
||||
let (data, response) = try await URLSession.shared.data(from: components.url!)
|
||||
guard let http = response as? HTTPURLResponse, http.statusCode == 200 else {
|
||||
throw FuelProviderError.relayUnavailable
|
||||
}
|
||||
let payload = try JSONDecoder().decode(RelayResponse.self, from: data)
|
||||
return payload.stations.map { relay in
|
||||
FuelStation(
|
||||
id: relay.id ?? relay.name ?? UUID().uuidString,
|
||||
name: relay.name ?? "Unknown",
|
||||
brand: relay.brand ?? "",
|
||||
address: relay.address ?? "",
|
||||
postcode: relay.postcode ?? "",
|
||||
lat: relay.lat ?? 0,
|
||||
lng: relay.lng ?? 0,
|
||||
prices: [fuel: relay.price],
|
||||
priceUpdated: nil
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private struct RelayResponse: Codable {
|
||||
struct RelayStation: Codable {
|
||||
let id: String?
|
||||
let name: String?
|
||||
let brand: String?
|
||||
let address: String?
|
||||
let postcode: String?
|
||||
let lat: Double?
|
||||
let lng: Double?
|
||||
let price: Double
|
||||
}
|
||||
let stations: [RelayStation]
|
||||
}
|
||||
|
||||
// MARK: - Sample provider (offline fallback only)
|
||||
|
||||
/// Representative stations spread across ENGLAND (one cluster per region) so
|
||||
/// the fallback works anywhere in the country — it is NOT tied to one town.
|
||||
/// Real England-wide data comes from the relay (full-UK Fuel Finder CSV/API).
|
||||
/// Prices in pence/litre.
|
||||
struct SampleFuelProvider: FuelPriceProviding {
|
||||
func fetchStations(near lat: Double?, lng: Double?, fuel: FuelType) async throws -> [FuelStation] {
|
||||
try await Task.sleep(nanoseconds: 300_000_000) // simulate fetch
|
||||
@@ -33,44 +90,65 @@ struct SampleFuelProvider: FuelPriceProviding {
|
||||
}
|
||||
|
||||
static let sampleStations: [FuelStation] = [
|
||||
FuelStation(id: "h1", name: "Tesco Express Halifax", brand: "Tesco",
|
||||
address: "12 Clare Road", postcode: "HX1 2HX",
|
||||
lat: 53.7200, lng: -1.8630,
|
||||
prices: [.e10: 138.9, .e5: 146.9, .diesel: 145.9],
|
||||
priceUpdated: nil),
|
||||
FuelStation(id: "h2", name: "Shell Skircoat Road", brand: "Shell",
|
||||
address: "245 Skircoat Road", postcode: "HX3 0HP",
|
||||
lat: 53.7120, lng: -1.8710,
|
||||
prices: [.e10: 142.9, .e5: 149.9, .diesel: 148.9],
|
||||
priceUpdated: nil),
|
||||
FuelStation(id: "h3", name: "Morrisons Halifax", brand: "Morrisons",
|
||||
// Yorkshire & the Humber (Halifax area)
|
||||
FuelStation(id: "y1", name: "Morrisons Halifax", brand: "Morrisons",
|
||||
address: "Haugh Shaw Road", postcode: "HX1 3TU",
|
||||
lat: 53.7265, lng: -1.8580,
|
||||
prices: [.e10: 137.9, .e5: 144.9, .diesel: 144.9],
|
||||
priceUpdated: nil),
|
||||
FuelStation(id: "h4", name: "BP Queensbury", brand: "BP",
|
||||
address: "316 High Street, Queensbury", postcode: "BD13 2NB",
|
||||
lat: 53.7550, lng: -1.8450,
|
||||
prices: [.e10: 140.9, .diesel: 147.9],
|
||||
priceUpdated: nil),
|
||||
FuelStation(id: "h5", name: "Asda Halifax", brand: "Asda",
|
||||
address: "Ovenden Way", postcode: "HX2 8DD",
|
||||
lat: 53.7340, lng: -1.8890,
|
||||
FuelStation(id: "y2", name: "Asda Leeds Crown Point", brand: "Asda",
|
||||
address: "Crown Point Road", postcode: "LS10 1ET",
|
||||
lat: 53.7800, lng: -1.5300,
|
||||
prices: [.e10: 136.9, .e5: 143.9, .diesel: 143.9],
|
||||
priceUpdated: nil),
|
||||
FuelStation(id: "h6", name: "Esso Sowerby Bridge", brand: "Esso",
|
||||
address: "Wharf Street", postcode: "HX6 2AH",
|
||||
lat: 53.7080, lng: -1.9080,
|
||||
prices: [.e10: 141.9, .e5: 148.9, .diesel: 147.9],
|
||||
FuelStation(id: "y3", name: "Tesco Express York", brand: "Tesco",
|
||||
address: "Fulford Road", postcode: "YO10 4AB",
|
||||
lat: 53.9500, lng: -1.0800,
|
||||
prices: [.e10: 138.9, .e5: 146.9, .diesel: 145.9],
|
||||
priceUpdated: nil),
|
||||
FuelStation(id: "h7", name: "Sainsbury's Elland", brand: "Sainsbury's",
|
||||
address: "Southgate", postcode: "HX5 0PA",
|
||||
lat: 53.6860, lng: -1.8380,
|
||||
// North West
|
||||
FuelStation(id: "nw1", name: "Sainsbury's Manchester", brand: "Sainsbury's",
|
||||
address: "Regent Road", postcode: "M5 3QH",
|
||||
lat: 53.4700, lng: -2.2800,
|
||||
prices: [.e10: 139.9, .e5: 147.9, .diesel: 146.9],
|
||||
priceUpdated: nil),
|
||||
FuelStation(id: "h8", name: "Gulf Brighouse", brand: "Gulf",
|
||||
address: "Bradford Road", postcode: "HD6 1RW",
|
||||
lat: 53.7000, lng: -1.7850,
|
||||
FuelStation(id: "nw2", name: "Shell Liverpool", brand: "Shell",
|
||||
address: "Rice Lane", postcode: "L9 1AD",
|
||||
lat: 53.4400, lng: -2.9600,
|
||||
prices: [.e10: 142.9, .e5: 149.9, .diesel: 148.9],
|
||||
priceUpdated: nil),
|
||||
// Midlands
|
||||
FuelStation(id: "m1", name: "Morrisons Birmingham", brand: "Morrisons",
|
||||
address: "Hagley Road", postcode: "B16 8NA",
|
||||
lat: 52.4700, lng: -1.9500,
|
||||
prices: [.e10: 137.9, .e5: 144.9, .diesel: 144.9],
|
||||
priceUpdated: nil),
|
||||
FuelStation(id: "m2", name: "Tesco Express Nottingham", brand: "Tesco",
|
||||
address: "Mansfield Road", postcode: "NG5 2DP",
|
||||
lat: 52.9900, lng: -1.1500,
|
||||
prices: [.e10: 138.9, .e5: 146.9, .diesel: 145.9],
|
||||
priceUpdated: nil),
|
||||
// South East / London
|
||||
FuelStation(id: "se1", name: "Asda Wembley", brand: "Asda",
|
||||
address: "Great Central Way", postcode: "HA9 0DB",
|
||||
lat: 51.5500, lng: -0.2800,
|
||||
prices: [.e10: 136.9, .e5: 143.9, .diesel: 143.9],
|
||||
priceUpdated: nil),
|
||||
FuelStation(id: "se2", name: "BP Brighton", brand: "BP",
|
||||
address: "Lewes Road", postcode: "BN2 3QB",
|
||||
lat: 50.8300, lng: -0.1100,
|
||||
prices: [.e10: 140.9, .diesel: 147.9],
|
||||
priceUpdated: nil),
|
||||
// South West
|
||||
FuelStation(id: "sw1", name: "Esso Bristol", brand: "Esso",
|
||||
address: "Wells Road", postcode: "BS4 2PP",
|
||||
lat: 51.4400, lng: -2.5700,
|
||||
prices: [.e10: 141.9, .e5: 148.9, .diesel: 147.9],
|
||||
priceUpdated: nil),
|
||||
// North East
|
||||
FuelStation(id: "ne1", name: "Gulf Newcastle", brand: "Gulf",
|
||||
address: "Scotswood Road", postcode: "NE4 7AA",
|
||||
lat: 54.9700, lng: -1.6400,
|
||||
prices: [.e10: 143.9, .e5: 151.9, .diesel: 149.9],
|
||||
priceUpdated: nil),
|
||||
]
|
||||
@@ -109,5 +187,13 @@ struct FuelFinderProvider: FuelPriceProviding {
|
||||
|
||||
enum FuelProviderError: LocalizedError {
|
||||
case notImplemented
|
||||
var errorDescription: String? { "Live Fuel Finder API not wired yet — activate with GOV.UK One Login credentials." }
|
||||
case relayUnavailable
|
||||
var errorDescription: String? {
|
||||
switch self {
|
||||
case .notImplemented:
|
||||
return "Live Fuel Finder API not wired yet — activate with GOV.UK One Login credentials."
|
||||
case .relayUnavailable:
|
||||
return "FuelBoard Relay unreachable — showing cached/sample data."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user