Fix watch build, status sync, and icon assets

This commit is contained in:
FuelBoard Contributor
2026-08-30 22:26:30 +01:00
parent ebda21436d
commit 09740fca18
55 changed files with 442 additions and 12 deletions
@@ -29,6 +29,132 @@
"idiom" : "universal",
"platform" : "ios",
"size" : "1024x1024"
},
{
"filename" : "Watch-NotificationCenter-48.png",
"idiom" : "watch",
"role" : "notificationCenter",
"scale" : "2x",
"size" : "24x24",
"subtype" : "38mm"
},
{
"filename" : "Watch-NotificationCenter-55.png",
"idiom" : "watch",
"role" : "notificationCenter",
"scale" : "2x",
"size" : "27.5x27.5",
"subtype" : "42mm"
},
{
"filename" : "Watch-CompanionSettings-58.png",
"idiom" : "watch",
"role" : "companionSettings",
"scale" : "2x",
"size" : "29x29"
},
{
"filename" : "Watch-CompanionSettings-87.png",
"idiom" : "watch",
"role" : "companionSettings",
"scale" : "3x",
"size" : "29x29"
},
{
"idiom" : "watch",
"role" : "notificationCenter",
"scale" : "2x",
"size" : "33x33",
"subtype" : "45mm"
},
{
"filename" : "Watch-AppLauncher-80.png",
"idiom" : "watch",
"role" : "appLauncher",
"scale" : "2x",
"size" : "40x40",
"subtype" : "38mm"
},
{
"filename" : "Watch-AppLauncher-88.png",
"idiom" : "watch",
"role" : "appLauncher",
"scale" : "2x",
"size" : "44x44",
"subtype" : "40mm"
},
{
"idiom" : "watch",
"role" : "appLauncher",
"scale" : "2x",
"size" : "46x46",
"subtype" : "41mm"
},
{
"filename" : "Watch-AppLauncher-100.png",
"idiom" : "watch",
"role" : "appLauncher",
"scale" : "2x",
"size" : "50x50",
"subtype" : "44mm"
},
{
"idiom" : "watch",
"role" : "appLauncher",
"scale" : "2x",
"size" : "51x51",
"subtype" : "45mm"
},
{
"idiom" : "watch",
"role" : "appLauncher",
"scale" : "2x",
"size" : "54x54",
"subtype" : "49mm"
},
{
"filename" : "Watch-QuickLook-172.png",
"idiom" : "watch",
"role" : "quickLook",
"scale" : "2x",
"size" : "86x86",
"subtype" : "38mm"
},
{
"filename" : "Watch-QuickLook-196.png",
"idiom" : "watch",
"role" : "quickLook",
"scale" : "2x",
"size" : "98x98",
"subtype" : "42mm"
},
{
"filename" : "Watch-QuickLook-216.png",
"idiom" : "watch",
"role" : "quickLook",
"scale" : "2x",
"size" : "108x108",
"subtype" : "44mm"
},
{
"idiom" : "watch",
"role" : "quickLook",
"scale" : "2x",
"size" : "117x117",
"subtype" : "45mm"
},
{
"idiom" : "watch",
"role" : "quickLook",
"scale" : "2x",
"size" : "129x129",
"subtype" : "49mm"
},
{
"filename" : "Watch-Marketing-1024.png",
"idiom" : "watch-marketing",
"scale" : "1x",
"size" : "1024x1024"
}
],
"info" : {
Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 278 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

-1
View File
@@ -6,7 +6,6 @@ import SwiftUI
struct FuelBoardApp: App {
init() {
WatchSyncManager.shared.activate()
SmartDataRefreshScheduler.register()
#if DEBUG
// QA hook (Debug builds only): `-qaLiveActivity e10|e5|diesel` starts a
// Live Activity with a long station name so the Lock Screen / island
+3
View File
@@ -46,6 +46,9 @@ final class WatchSyncManager: NSObject, WCSessionDelegate {
if let favourites = try? JSONEncoder().encode(FuelStore.loadFavourites()) {
context[FuelStore.favouritesKey] = favourites
}
if let stations = try? JSONEncoder().encode(FuelStore.loadStations()) {
context[FuelStore.stationsKey] = stations
}
context[FuelStore.fuelKey] = FuelStore.loadSelectedFuel().rawValue
context[FuelStore.distanceUnitKey] = FuelStore.loadDistanceUnit().rawValue
@@ -1,9 +1,146 @@
{
"images" : [
{
"idiom" : "universal",
"platform" : "watchos",
"filename" : "Watch-NotificationCenter-48.png",
"idiom" : "watch",
"role" : "notificationCenter",
"scale" : "2x",
"size" : "24x24",
"subtype" : "38mm"
},
{
"filename" : "Watch-NotificationCenter-55.png",
"idiom" : "watch",
"role" : "notificationCenter",
"scale" : "2x",
"size" : "27.5x27.5",
"subtype" : "42mm"
},
{
"filename" : "Watch-CompanionSettings-58.png",
"idiom" : "watch",
"role" : "companionSettings",
"scale" : "2x",
"size" : "29x29"
},
{
"filename" : "Watch-CompanionSettings-87.png",
"idiom" : "watch",
"role" : "companionSettings",
"scale" : "3x",
"size" : "29x29"
},
{
"idiom" : "watch",
"role" : "notificationCenter",
"scale" : "2x",
"size" : "33x33",
"subtype" : "45mm"
},
{
"filename" : "Watch-AppLauncher-80.png",
"idiom" : "watch",
"role" : "appLauncher",
"scale" : "2x",
"size" : "40x40",
"subtype" : "38mm"
},
{
"filename" : "Watch-AppLauncher-88.png",
"idiom" : "watch",
"role" : "appLauncher",
"scale" : "2x",
"size" : "44x44",
"subtype" : "40mm"
},
{
"idiom" : "watch",
"role" : "appLauncher",
"scale" : "2x",
"size" : "46x46",
"subtype" : "41mm"
},
{
"filename" : "Watch-AppLauncher-100.png",
"idiom" : "watch",
"role" : "appLauncher",
"scale" : "2x",
"size" : "50x50",
"subtype" : "44mm"
},
{
"idiom" : "watch",
"role" : "appLauncher",
"scale" : "2x",
"size" : "51x51",
"subtype" : "45mm"
},
{
"idiom" : "watch",
"role" : "appLauncher",
"scale" : "2x",
"size" : "54x54",
"subtype" : "49mm"
},
{
"filename" : "Watch-QuickLook-172.png",
"idiom" : "watch",
"role" : "quickLook",
"scale" : "2x",
"size" : "86x86",
"subtype" : "38mm"
},
{
"filename" : "Watch-QuickLook-196.png",
"idiom" : "watch",
"role" : "quickLook",
"scale" : "2x",
"size" : "98x98",
"subtype" : "42mm"
},
{
"filename" : "Watch-QuickLook-216.png",
"idiom" : "watch",
"role" : "quickLook",
"scale" : "2x",
"size" : "108x108",
"subtype" : "44mm"
},
{
"idiom" : "watch",
"role" : "quickLook",
"scale" : "2x",
"size" : "117x117",
"subtype" : "45mm"
},
{
"idiom" : "watch",
"role" : "quickLook",
"scale" : "2x",
"size" : "129x129",
"subtype" : "49mm"
},
{
"filename" : "Watch-Marketing-1024.png",
"idiom" : "watch-marketing",
"scale" : "1x",
"size" : "1024x1024"
},
{
"filename" : "Watch-LongLook-88.png",
"idiom" : "watch",
"role" : "longLook",
"scale" : "2x",
"size" : "44x44",
"subtype" : "42mm"
},
{
"filename" : "Watch-LongLook-100.png",
"idiom" : "watch",
"role" : "longLook",
"scale" : "2x",
"size" : "50x50",
"subtype" : "44mm"
}
],
"info" : {
Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 162 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 172 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 172 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 172 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 172 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 189 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 278 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

+3 -7
View File
@@ -392,12 +392,6 @@ struct ContentView: View {
.foregroundStyle(.secondary)
}
Spacer()
Text(fuel.shortName)
.font(.caption)
.fontWeight(.bold)
.padding(.horizontal, 8)
.padding(.vertical, 4)
.background(fuel.color.opacity(0.18), in: Capsule())
}
}
@@ -496,7 +490,9 @@ struct ContentView: View {
}
private func statusFromCache() -> WatchRefreshState {
guard !stations.isEmpty else { return .noData }
let hasUsablePrices = !stations.isEmpty
|| favourites.contains { favourite in favourite.station.prices[fuel] != nil }
guard hasUsablePrices else { return .noData }
guard let lastRefresh = WatchFuelCache.loadLastRefresh() else { return .stale(lastUpdated: "earlier") }
let formatted = relativeRefreshText(for: lastRefresh)
return WatchFuelCache.isCacheFresh(lastRefresh: lastRefresh)
@@ -46,6 +46,9 @@ final class WatchSyncManager: NSObject, WCSessionDelegate {
if let favourites = payload["fuelboard.favourites"] as? Data {
defaults.set(favourites, forKey: "fuelboard.favourites")
}
if let stations = payload["fuelboard.stations"] as? Data {
defaults.set(stations, forKey: "fuelboard.stations")
}
if let fuel = payload["fuelboard.selectedFuel"] as? String {
defaults.set(fuel, forKey: "fuelboard.selectedFuel")
}
@@ -1,8 +1,135 @@
{
"images" : [
{
"idiom" : "universal",
"platform" : "watchos",
"filename" : "Watch-NotificationCenter-48.png",
"idiom" : "watch",
"role" : "notificationCenter",
"scale" : "2x",
"size" : "24x24",
"subtype" : "38mm"
},
{
"filename" : "Watch-NotificationCenter-55.png",
"idiom" : "watch",
"role" : "notificationCenter",
"scale" : "2x",
"size" : "27.5x27.5",
"subtype" : "42mm"
},
{
"filename" : "Watch-CompanionSettings-58.png",
"idiom" : "watch",
"role" : "companionSettings",
"scale" : "2x",
"size" : "29x29"
},
{
"filename" : "Watch-CompanionSettings-87.png",
"idiom" : "watch",
"role" : "companionSettings",
"scale" : "3x",
"size" : "29x29"
},
{
"filename" : "Watch-NotificationCenter-66.png",
"idiom" : "watch",
"role" : "notificationCenter",
"scale" : "2x",
"size" : "33x33",
"subtype" : "45mm"
},
{
"filename" : "Watch-AppLauncher-80.png",
"idiom" : "watch",
"role" : "appLauncher",
"scale" : "2x",
"size" : "40x40",
"subtype" : "38mm"
},
{
"filename" : "Watch-AppLauncher-88.png",
"idiom" : "watch",
"role" : "appLauncher",
"scale" : "2x",
"size" : "44x44",
"subtype" : "40mm"
},
{
"filename" : "Watch-AppLauncher-92.png",
"idiom" : "watch",
"role" : "appLauncher",
"scale" : "2x",
"size" : "46x46",
"subtype" : "41mm"
},
{
"filename" : "Watch-AppLauncher-100.png",
"idiom" : "watch",
"role" : "appLauncher",
"scale" : "2x",
"size" : "50x50",
"subtype" : "44mm"
},
{
"filename" : "Watch-AppLauncher-102.png",
"idiom" : "watch",
"role" : "appLauncher",
"scale" : "2x",
"size" : "51x51",
"subtype" : "45mm"
},
{
"filename" : "Watch-AppLauncher-108.png",
"idiom" : "watch",
"role" : "appLauncher",
"scale" : "2x",
"size" : "54x54",
"subtype" : "49mm"
},
{
"filename" : "Watch-QuickLook-172.png",
"idiom" : "watch",
"role" : "quickLook",
"scale" : "2x",
"size" : "86x86",
"subtype" : "38mm"
},
{
"filename" : "Watch-QuickLook-196.png",
"idiom" : "watch",
"role" : "quickLook",
"scale" : "2x",
"size" : "98x98",
"subtype" : "42mm"
},
{
"filename" : "Watch-QuickLook-216.png",
"idiom" : "watch",
"role" : "quickLook",
"scale" : "2x",
"size" : "108x108",
"subtype" : "44mm"
},
{
"filename" : "Watch-QuickLook-234.png",
"idiom" : "watch",
"role" : "quickLook",
"scale" : "2x",
"size" : "117x117",
"subtype" : "45mm"
},
{
"filename" : "Watch-QuickLook-258.png",
"idiom" : "watch",
"role" : "quickLook",
"scale" : "2x",
"size" : "129x129",
"subtype" : "49mm"
},
{
"filename" : "Watch-Marketing-1024.png",
"idiom" : "watch-marketing",
"scale" : "1x",
"size" : "1024x1024"
}
],
Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 278 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

+39
View File
@@ -0,0 +1,39 @@
# FuelBoard Release Notes
## Next TestFlight build
### Summary
- Added a new Data checking setting with Standard and Smart modes.
- Clarified the behavior and background-update expectations in Settings.
### What changed
- Added a dedicated **Data checking** section in Settings.
- Added two user-facing modes:
- **Standard** — Saves battery. Refreshes price data up to twice daily.
- **Smart** — Checks for newer data more often in the background and refreshes full prices only when an update is available.
- Smart mode now uses a lightweight metadata probe first, then only refreshes the full dataset if newer data is available.
- Added clear explanatory copy that background checks are best-effort and happen only when iOS allows.
- Kept larger refresh behavior conservative rather than implying exact hourly updates.
### Why
- The previous refresh model was conservative but not very configurable.
- The new setting makes the behavior clearer and gives users a choice between battery-friendly default behavior and a smarter background-check mode.
- The wording is designed to be honest about iOS background limits.
### Suggested TestFlight notes
- Added new Data checking modes in Settings: Standard and Smart.
- Smart mode now checks for newer data more intelligently in the background when iOS allows.
- Improved settings copy to better explain data freshness behavior.
### Suggested What to Test
- Open Settings and confirm **Data checking** appears as its own section.
- Confirm the two options read clearly and feel understandable:
- Standard
- Smart
- Switch between Standard and Smart and confirm the selection persists.
- Confirm the explanatory copy feels accurate and not misleading about iOS background behavior.
### Implementation note
- Standard keeps the existing cache-first refresh behavior.
- Smart adds a lightweight probe path before deciding whether to download the full data set.
- Background timing remains best-effort under iOS scheduling.