Live Activity: cheapest-in-radius Lock Screen/Dynamic Island pill
- Shared FuelBoardLiveActivityAttributes/ContentState (app + widget targets) - FuelBoardLiveActivity Widget: ActivityConfiguration rendered by the widget extension - Lock Screen banner + Dynamic Island (leading/trailing/bottom, compact, minimal), tap anywhere -> maps:// directions to the best station - LiveActivityManager (app side): request/update/end; mirrors the app list's TOP badge (selected fuel + chosen distance radius, price-then-distance ties); local start-date tracking for the 8h cap (this SDK's Activity has no startDate) - auto-restarts at 7h59m; silent no-op when disabled/denied - Wiring: 250m/60s location hook (incl. background significant-change wake-ups), refresh completion, fuel/radius/unit changes, toggle flip - Settings toggle + FuelStore persistence (keychain-backed, app-group) - App-Info.plist: NSSupportsLiveActivities
This commit is contained in:
@@ -0,0 +1,32 @@
|
||||
// FuelBoardLiveActivity.swift — Live Activity shared model.
|
||||
//
|
||||
// Compiled into BOTH the app target (to start/update/end the activity) and
|
||||
// the widget extension (to render it). ActivityKit is iOS-only, so this file
|
||||
// is deliberately NOT symlinked into FuelBoardTests/Sources — the SPM test
|
||||
// target runs on macOS where ActivityKit does not exist.
|
||||
//
|
||||
// The activity mirrors the app's TOP badge: the cheapest station selling the
|
||||
// app's selected fuel within the app's chosen distance radius. Tapping the
|
||||
// activity opens Apple Maps driving directions to that station (maps://).
|
||||
|
||||
import ActivityKit
|
||||
import Foundation
|
||||
|
||||
/// Attributes are fixed at request time. Here they pin the FUEL being
|
||||
/// tracked; everything that can change while driving (station, price,
|
||||
/// distance) lives in ContentState so updates don't need a new activity.
|
||||
struct FuelBoardLiveActivityAttributes: ActivityAttributes {
|
||||
var fuel: FuelType
|
||||
|
||||
public struct ContentState: Codable, Hashable {
|
||||
var stationID: String
|
||||
var stationName: String
|
||||
var brand: String
|
||||
/// Price in pence per litre (same unit as `FuelStation.prices`).
|
||||
var pricePence: Double
|
||||
var distanceKM: Double
|
||||
var lat: Double
|
||||
var lng: Double
|
||||
var updatedAt: Date
|
||||
}
|
||||
}
|
||||
@@ -262,6 +262,7 @@ struct FuelStore {
|
||||
static let alertsRadiusKey = "fuelboard.alertsRadius" // Double km
|
||||
static let alertsFuelKey = "fuelboard.alertsFuel" // FuelType raw value
|
||||
static let debugModeKey = "fuelboard.debugMode" // Bool — hidden dev flag
|
||||
static let liveActivityKey = "fuelboard.liveActivity" // Bool — Live Activity toggle
|
||||
static let onboardingCompletedKey = "fuelboard.onboardingCompleted" // Bool
|
||||
static let lastRefreshKey = "fuelboard.lastRefresh" // TimeInterval (seconds since 1970)
|
||||
static let relaySourceKey = "fuelboard.relaySource" // String — "api" | "csv"
|
||||
@@ -490,6 +491,16 @@ struct FuelStore {
|
||||
saveString(String(radius), service: alertsRadiusKey)
|
||||
}
|
||||
|
||||
// MARK: Live Activity
|
||||
|
||||
static func loadLiveActivityEnabled() -> Bool {
|
||||
loadString(service: liveActivityKey) == "1"
|
||||
}
|
||||
|
||||
static func saveLiveActivityEnabled(_ enabled: Bool) {
|
||||
saveString(enabled ? "1" : "0", service: liveActivityKey)
|
||||
}
|
||||
|
||||
// MARK: Refresh policy — data is cached; the app only auto-refreshes
|
||||
// twice a day (pull-to-refresh is the manual override).
|
||||
|
||||
|
||||
Reference in New Issue
Block a user