widget + live activity: prefer Apple-Maps road distance, computed by the app
The widget/Live Activity showed straight-line haversine distance (0.8 mi) while Apple Maps routes 1.8 mi. Road routing is too heavy for the widget execution + ~40-70/day refresh budget, so the APP now computes it: - New RoadDistanceService (app target): for the nearest 12 stations each pass, calls MapKit MKDirections (free, no API key, matches Apple Maps) and caches metres keyed by station ID. - Cache stored in KEYCHAIN (fuelboard.roadDistances) so the widget reads it even on free SideStore accounts with no app-group container; only valid within 600 m of the location it was built from. - Throttled: recompute max every 10 min, or when the user moves > 400 m; wired into the location-update hook + location onChange. - Widget face, app station rows + Live Activity show the cached road distance, falling back to straight-line when absent. - 4 new cache tests (105 total).
This commit is contained in:
@@ -620,3 +620,46 @@ final class OfflineDataLabelTests: XCTestCase {
|
||||
XCTAssertNil(FuelStore.offlineDataLabel(from: "not-a-date"))
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Road distance cache
|
||||
|
||||
final class RoadDistanceCacheTests: XCTestCase {
|
||||
private func station(_ id: String, _ lat: Double, _ lng: Double) -> FuelStation {
|
||||
FuelStation(id: id, name: id, brand: "X", address: "", postcode: "",
|
||||
lat: lat, lng: lng, prices: [:], priceUpdated: nil)
|
||||
}
|
||||
|
||||
func testDisplayDistanceFallsBackToStraightLineWhenNoCache() {
|
||||
// London user, station ~ London -> no cache -> straight-line haversine.
|
||||
let s = station("a", 51.5074, -0.1278)
|
||||
let km = FuelStore.displayDistanceKM(station: s, userLat: 51.6, userLng: -0.1)
|
||||
XCTAssertEqual(km, s.distanceKM(to: 51.6, lng2: -0.1), accuracy: 0.0001)
|
||||
}
|
||||
|
||||
func testRoadDistanceUsedWhenCachedNear() {
|
||||
let s = station("a", 51.5074, -0.1278)
|
||||
// Cache a road distance of 3.2 km for this station from the user's fix.
|
||||
FuelStore.saveRoadDistances(sourceLat: 51.6, sourceLng: -0.1, entries: ["a": 3200])
|
||||
let km = FuelStore.displayDistanceKM(station: s, userLat: 51.6, userLng: -0.1)
|
||||
XCTAssertEqual(km, 3.2, accuracy: 0.0001)
|
||||
}
|
||||
|
||||
func testRoadDistanceNilWhenOriginFar() {
|
||||
let s = station("a", 51.5074, -0.1278)
|
||||
// Cache built in London, but the user is now ~200 km away -> stale.
|
||||
FuelStore.saveRoadDistances(sourceLat: 51.5074, sourceLng: -0.1278, entries: ["a": 3200])
|
||||
let meters = FuelStore.roadDistanceMeters(for: "a", userLat: 53.4808, userLng: -2.2426)
|
||||
XCTAssertNil(meters)
|
||||
// And display falls back to straight-line.
|
||||
let km = FuelStore.displayDistanceKM(station: s, userLat: 53.4808, userLng: -2.2426)
|
||||
XCTAssertEqual(km, s.distanceKM(to: 53.4808, lng2: -2.2426), accuracy: 0.0001)
|
||||
}
|
||||
|
||||
func testRoadDistanceUsedForOtherStationNotFound() {
|
||||
FuelStore.saveRoadDistances(sourceLat: 51.6, sourceLng: -0.1, entries: ["a": 3200])
|
||||
// A station that isn't in the cache falls back to straight-line.
|
||||
let s = station("z", 51.51, -0.13)
|
||||
let km = FuelStore.displayDistanceKM(station: s, userLat: 51.6, userLng: -0.1)
|
||||
XCTAssertEqual(km, s.distanceKM(to: 51.6, lng2: -0.1), accuracy: 0.0001)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user