App Store prep: privacy manifests, 1.0.1 (2), OGL attribution, UK empty state, screenshot harness
- PrivacyInfo.xcprivacy in app + widget targets (UserDefaults 1C8F.1; app declares Location linked + Diagnostics not-linked, no tracking) - Version bump 1.0.1 (2) in App + Widget plists - Settings → Data source footer: OGL attribution + 'refresh up to twice a day' - Stations empty state: friendly UK-only explainer + 'Browse all UK stations' - ContentView launch-arg harness (pattern of -showKeySheet): -skipOnboarding and -tab stations|favourites|alerts|settings for screenshot capture - APPSTORE.md updated (blockers struck, tasks done, yuzu-hub workflow) - BACKLOG.md hygiene: GitHub-first header, Trends merged note, offline-first-run DONE
This commit is contained in:
+24
-17
@@ -24,9 +24,8 @@ The two 2026-08-11 blockers are RESOLVED by P0 (live chain):
|
|||||||
2. **[USER] Register App ID `com.apt.fuelboard`** in the developer portal,
|
2. **[USER] Register App ID `com.apt.fuelboard`** in the developer portal,
|
||||||
with the **App Group `group.com.apt.fuelboard`** capability enabled (the
|
with the **App Group `group.com.apt.fuelboard`** capability enabled (the
|
||||||
widget + app-group cache depend on it).
|
widget + app-group cache depend on it).
|
||||||
3. **[ME] `PrivacyInfo.xcprivacy`** — REQUIRED for all submissions since
|
3. ~~[ME] `PrivacyInfo.xcprivacy`~~ — **DONE 2026-08-16** (app + widget
|
||||||
May 2024; the repo has none. Add to the app target (Xcode can generate;
|
manifests added; UserDefaults `1C8F.1`, Location + Diagnostics declared).
|
||||||
must declare location-data reasons + any required-reason APIs used).
|
|
||||||
4. **[ME] Real Distribution signing** — Distribution certificate + App Store
|
4. **[ME] Real Distribution signing** — Distribution certificate + App Store
|
||||||
provisioning profile (or Xcode automatic signing once the account is in
|
provisioning profile (or Xcode automatic signing once the account is in
|
||||||
Xcode); then `xcodebuild archive` (no more `CODE_SIGNING_ALLOWED=NO`).
|
Xcode); then `xcodebuild archive` (no more `CODE_SIGNING_ALLOWED=NO`).
|
||||||
@@ -35,21 +34,29 @@ The two 2026-08-11 blockers are RESOLVED by P0 (live chain):
|
|||||||
|
|
||||||
## Required tasks — owner [ME] (technical, do after account exists)
|
## Required tasks — owner [ME] (technical, do after account exists)
|
||||||
|
|
||||||
- [ ] PrivacyInfo.xcprivacy (blocker #3 above)
|
- [x] **PrivacyInfo.xcprivacy** — app + widget manifests added (2026-08-16):
|
||||||
- [ ] Distribution archive + TestFlight upload
|
UserDefaults reason `1C8F.1`; app also declares Location (linked, not
|
||||||
- [ ] Bump version: `CFBundleShortVersionString 1.0.1` / `CFBundleVersion 2`
|
tracking) + Diagnostics (not linked, not tracking — the dev beacon)
|
||||||
- [ ] OGL attribution + "prices updated twice daily" footnote in
|
- [ ] Distribution archive + TestFlight upload (needs Apple account)
|
||||||
Settings → About (2.3.1 hygiene; data is 12 h-cache, UI says "cheapest")
|
- [x] **Version bump** → `1.0.1` (build 2), app + widget plists
|
||||||
- [ ] Out-of-UK empty state: reviewer may not be in the UK → friendlier copy
|
- [x] **OGL attribution** — Settings → Data source footer now reads "Prices
|
||||||
than "No X stations found." (explain UK-only data + browse-all option)
|
use the UK Fuel Finder dataset (Open Government Licence) and refresh
|
||||||
- [ ] Optional hardening: gate the dev-only surface (Settings Debug section,
|
up to twice a day." (2.3.1 hygiene)
|
||||||
`fuelboard.relayFallback`, FuelBeacon, relay IP) behind `#if DEBUG` so
|
- [x] **Out-of-UK empty state** — "No X stations within N miles" + UK-only
|
||||||
the Release binary ships clean — currently Release contains the hidden
|
explainer + "Browse all UK stations" button (switches to Closest)
|
||||||
dev flag + a private IP string (no credentials, but a "test build"
|
- [ ] ~~Gate dev surface behind #if DEBUG~~ **DECLINED — kept in Release on
|
||||||
smell; not a rejection risk on its own)
|
purpose**: the user's home-testing installs the Release IPA and toggles
|
||||||
|
`fuelboard.relayFallback` via Settings → Debug (revealed only by tapping
|
||||||
|
About → Version ×5 — invisible to reviewers; no credentials involved).
|
||||||
|
Gating it out of Release would break that workflow for zero review gain.
|
||||||
- [ ] Final pass: `swift test` green (95 tests), archive smoke test on device
|
- [ ] Final pass: `swift test` green (95 tests), archive smoke test on device
|
||||||
- [ ] Capture screenshots from simulator (6.7″ 1290×2796 required; 6.5″
|
- [ ] ~~Capture screenshots from simulator~~ **RAW CAPTURES DONE (2026-08-16)** —
|
||||||
1242×2688 recommended) for App Store Connect
|
native 6.9″ 1320×2868 PNGs of all four tabs (Stations, Favourites,
|
||||||
|
Alerts, Settings) at `/tmp/fuelboard-shots/asc/`; assembled into ASC
|
||||||
|
frames by the user in the yuzu-hub App Store Screenshot Generator
|
||||||
|
(https://yuzu-hub.github.io/appscreen/). Screenshot harness: launch
|
||||||
|
args `-skipOnboarding` / `-tab stations|favourites|alerts|settings`
|
||||||
|
(ContentView, same pattern as `-showKeySheet`).
|
||||||
|
|
||||||
## Required tasks — owner [USER] (App Store Connect, cannot be done by me)
|
## Required tasks — owner [USER] (App Store Connect, cannot be done by me)
|
||||||
|
|
||||||
|
|||||||
+12
-10
@@ -1,8 +1,10 @@
|
|||||||
# FuelBoard Backlog
|
# FuelBoard Backlog
|
||||||
|
|
||||||
FuelBoard — England-wide petrol-station price widget + app.
|
FuelBoard — England-wide petrol-station price widget + app.
|
||||||
Relay mirrors the UK gov data (hourly CSV); app fetches the full-UK dump
|
GitHub mirror (`aptonline/fuelboard-data`, daily) holds the full-UK dump
|
||||||
(~8,038 stations, all prices) once per 12 h and filters/sorts on-device.
|
(~8,022 stations, all prices); app fetches it once per 12 h and filters/
|
||||||
|
sorts on-device, with a bundled snapshot as the no-network last resort.
|
||||||
|
LAN relay is dev-only (hidden `fuelboard.relayFallback` flag).
|
||||||
|
|
||||||
Priority guide: P0 = next build, P1 = soon, P2 = later, P3 = maybe/idea.
|
Priority guide: P0 = next build, P1 = soon, P2 = later, P3 = maybe/idea.
|
||||||
Status: TODO / IN PROGRESS / DONE / BLOCKED.
|
Status: TODO / IN PROGRESS / DONE / BLOCKED.
|
||||||
@@ -15,9 +17,8 @@ Status: TODO / IN PROGRESS / DONE / BLOCKED.
|
|||||||
(App Intents, iOS 16+), dialog + price-card snippet, cached full-UK dump first
|
(App Intents, iOS 16+), dialog + price-card snippet, cached full-UK dump first
|
||||||
with freshness label ("as of 6am"), last-known-location fallback. Constraint:
|
with freshness label ("as of 6am"), last-known-location fallback. Constraint:
|
||||||
fresh-data path needs a reachable source off-LAN — cached answers work anywhere
|
fresh-data path needs a reachable source off-LAN — cached answers work anywhere
|
||||||
the app has previously fetched (see P3 data mirror). Interim: the relay on
|
the app has previously fetched (the GitHub mirror serves this now). *DONE —
|
||||||
Tailscale (100.120.217.98:8789) is reachable off-LAN for testing; relay will
|
radius-scoped
|
||||||
move to a permanent box or the GitHub mirror long-term. *DONE — radius-scoped
|
|
||||||
lookup `037c241` (real-data regression: GULF HISTON 100.9p 132 mi away),
|
lookup `037c241` (real-data regression: GULF HISTON 100.9p 132 mi away),
|
||||||
per-fuel literal-word shortcuts `e55078c`, names = app words `a3b6f37`, plus
|
per-fuel literal-word shortcuts `e55078c`, names = app words `a3b6f37`, plus
|
||||||
Directions-to-cheapest `caadf18` + spoken hand-off `aed99e3`.*
|
Directions-to-cheapest `caadf18` + spoken hand-off `aed99e3`.*
|
||||||
@@ -31,7 +32,7 @@ Status: TODO / IN PROGRESS / DONE / BLOCKED.
|
|||||||
capsule fuel picker; range control 7/30/90 days; respects the price-display
|
capsule fuel picker; range control 7/30/90 days; respects the price-display
|
||||||
toggle; honest empty-state copy in Localizable.strings ("Prices are recorded from
|
toggle; honest empty-state copy in Localizable.strings ("Prices are recorded from
|
||||||
each refresh — check back in a few days"). No widget in v1 (one-kind rule).
|
each refresh — check back in a few days"). No widget in v1 (one-kind rule).
|
||||||
Depends on the P0 archive. *DONE 2026-08-15 on feature/trends-history (unmerged):
|
Depends on the P0 archive. *DONE 2026-08-15, merged to main 2026-08-16:
|
||||||
`Shared/FuelHistory.swift` (FuelHistoryStore: day math, slim day decode w/ shared
|
`Shared/FuelHistory.swift` (FuelHistoryStore: day math, slim day decode w/ shared
|
||||||
band, series + deltaSeries, app-group favourites-only cache, parallel per-day
|
band, series + deltaSeries, app-group favourites-only cache, parallel per-day
|
||||||
fetches) + `FuelBoard/TrendsView.swift` (chart, fuel capsule, 7/30/90, Price/vs
|
fetches) + `FuelBoard/TrendsView.swift` (chart, fuel capsule, 7/30/90, Price/vs
|
||||||
@@ -39,8 +40,8 @@ Status: TODO / IN PROGRESS / DONE / BLOCKED.
|
|||||||
FavouritesView + strings. 83 tests incl. URL regression (appendingPathComponent
|
FavouritesView + strings. 83 tests incl. URL regression (appendingPathComponent
|
||||||
— URL(string:relativeTo:) dropped /main, all fetches 404'd). Sim-verified:
|
— URL(string:relativeTo:) dropped /main, all fetches 404'd). Sim-verified:
|
||||||
favourites rows + TOP/deltas; sheet controls + honest empty state w/ live first-
|
favourites rows + TOP/deltas; sheet controls + honest empty state w/ live first-
|
||||||
snapshot date. Pending: merge to main after device sideload test; lines render
|
snapshot date. Pending: lines render once ≥2 snapshots (archive warming daily,
|
||||||
once ≥2 snapshots (archive warming daily).*
|
~Aug 17).*
|
||||||
|
|
||||||
## P2 — Later
|
## P2 — Later
|
||||||
|
|
||||||
@@ -82,8 +83,9 @@ Status: TODO / IN PROGRESS / DONE / BLOCKED.
|
|||||||
user-configurable (currently fixed 300 m trigger / 1 per station per hour).
|
user-configurable (currently fixed 300 m trigger / 1 per station per hour).
|
||||||
- [ ] **Pull-to-refresh spinner state** — surface refresh in-flight state
|
- [ ] **Pull-to-refresh spinner state** — surface refresh in-flight state
|
||||||
(currently `refreshable` fires but no visible progress in the row list).
|
(currently `refreshable` fires but no visible progress in the row list).
|
||||||
- [ ] **Offline first-run** — first launch without network currently shows empty
|
- [x] **Offline first-run** — bundled REAL 8,022-station dump (`FuelBoardDump`,
|
||||||
states; bundle a sample dump so the app demos offline.
|
refreshed by `scripts/refresh_bundled_dump.sh`) is the no-network last resort
|
||||||
|
in the live chain (P0, 2026-08-16); first launch demos the full dataset offline.
|
||||||
- [ ] **Localization** — UK English only today; audit string catalog for l10n.
|
- [ ] **Localization** — UK English only today; audit string catalog for l10n.
|
||||||
- [ ] **Dark mode polish** — verify brand logos (white-circle clips) and RAG dot
|
- [ ] **Dark mode polish** — verify brand logos (white-circle clips) and RAG dot
|
||||||
contrast under dark appearance on-device.
|
contrast under dark appearance on-device.
|
||||||
|
|||||||
@@ -16,6 +16,8 @@
|
|||||||
<string>$(PRODUCT_NAME)</string>
|
<string>$(PRODUCT_NAME)</string>
|
||||||
<key>CFBundlePackageType</key>
|
<key>CFBundlePackageType</key>
|
||||||
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
|
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
|
||||||
|
<key>CFBundleShortVersionString</key>
|
||||||
|
<string>1.0.1</string>
|
||||||
<key>CFBundleURLTypes</key>
|
<key>CFBundleURLTypes</key>
|
||||||
<array>
|
<array>
|
||||||
<dict>
|
<dict>
|
||||||
@@ -27,18 +29,16 @@
|
|||||||
</array>
|
</array>
|
||||||
</dict>
|
</dict>
|
||||||
</array>
|
</array>
|
||||||
<key>CFBundleShortVersionString</key>
|
|
||||||
<string>1.0</string>
|
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>1</string>
|
<string>2</string>
|
||||||
<key>LSRequiresIPhoneOS</key>
|
<key>LSRequiresIPhoneOS</key>
|
||||||
<true/>
|
<true/>
|
||||||
<key>NSLocationWhenInUseUsageDescription</key>
|
|
||||||
<string>FuelBoard uses your location to find the cheapest nearby petrol stations.</string>
|
|
||||||
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
|
|
||||||
<string>FuelBoard uses Always location to alert you when you approach the cheapest station nearby.</string>
|
|
||||||
<key>NSLocalNetworkUsageDescription</key>
|
<key>NSLocalNetworkUsageDescription</key>
|
||||||
<string>FuelBoard can optionally connect to the FuelBoard Relay on your local network for faster alerts during development.</string>
|
<string>FuelBoard can optionally connect to the FuelBoard Relay on your local network for faster alerts during development.</string>
|
||||||
|
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
|
||||||
|
<string>FuelBoard uses Always location to alert you when you approach the cheapest station nearby.</string>
|
||||||
|
<key>NSLocationWhenInUseUsageDescription</key>
|
||||||
|
<string>FuelBoard uses your location to find the cheapest nearby petrol stations.</string>
|
||||||
<key>NSSupportsLiveActivities</key>
|
<key>NSSupportsLiveActivities</key>
|
||||||
<true/>
|
<true/>
|
||||||
<key>UIBackgroundModes</key>
|
<key>UIBackgroundModes</key>
|
||||||
|
|||||||
@@ -17,15 +17,15 @@
|
|||||||
<key>CFBundlePackageType</key>
|
<key>CFBundlePackageType</key>
|
||||||
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
|
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
<string>1.0</string>
|
<string>1.0.1</string>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>1</string>
|
<string>2</string>
|
||||||
<key>NSLocationWhenInUseUsageDescription</key>
|
|
||||||
<string>FuelBoard uses your location to show the cheapest nearby petrol stations.</string>
|
|
||||||
<key>NSExtension</key>
|
<key>NSExtension</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>NSExtensionPointIdentifier</key>
|
<key>NSExtensionPointIdentifier</key>
|
||||||
<string>com.apple.widgetkit-extension</string>
|
<string>com.apple.widgetkit-extension</string>
|
||||||
</dict>
|
</dict>
|
||||||
|
<key>NSLocationWhenInUseUsageDescription</key>
|
||||||
|
<string>FuelBoard uses your location to show the cheapest nearby petrol stations.</string>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ struct ContentView: View {
|
|||||||
@State private var isLoading = false
|
@State private var isLoading = false
|
||||||
@State private var statusMessage = ""
|
@State private var statusMessage = ""
|
||||||
@State private var showOnboarding = false
|
@State private var showOnboarding = false
|
||||||
|
@State private var selectedTab = 0
|
||||||
@State private var locationManager = LocationManager()
|
@State private var locationManager = LocationManager()
|
||||||
@StateObject private var monitor = ProximityMonitor()
|
@StateObject private var monitor = ProximityMonitor()
|
||||||
|
|
||||||
@@ -137,12 +138,14 @@ struct ContentView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
TabView {
|
TabView(selection: $selectedTab) {
|
||||||
stationsTab
|
stationsTab
|
||||||
.tabItem { Label("Stations", systemImage: "fuelpump.fill") }
|
.tabItem { Label("Stations", systemImage: "fuelpump.fill") }
|
||||||
|
.tag(0)
|
||||||
|
|
||||||
favouritesTab
|
favouritesTab
|
||||||
.tabItem { Label("Favourites", systemImage: "star.fill") }
|
.tabItem { Label("Favourites", systemImage: "star.fill") }
|
||||||
|
.tag(1)
|
||||||
|
|
||||||
AlertsView(
|
AlertsView(
|
||||||
enabled: $alertsEnabled,
|
enabled: $alertsEnabled,
|
||||||
@@ -159,8 +162,10 @@ struct ContentView: View {
|
|||||||
lastAlert: monitor.lastAlert
|
lastAlert: monitor.lastAlert
|
||||||
)
|
)
|
||||||
.tabItem { Label("Alerts", systemImage: "bell.fill") }
|
.tabItem { Label("Alerts", systemImage: "bell.fill") }
|
||||||
|
.tag(2)
|
||||||
|
|
||||||
settingsTab
|
settingsTab
|
||||||
|
.tag(3)
|
||||||
}
|
}
|
||||||
.fullScreenCover(isPresented: $showOnboarding) {
|
.fullScreenCover(isPresented: $showOnboarding) {
|
||||||
OnboardingView {
|
OnboardingView {
|
||||||
@@ -171,11 +176,28 @@ struct ContentView: View {
|
|||||||
StationMapView(request: request)
|
StationMapView(request: request)
|
||||||
}
|
}
|
||||||
.onAppear {
|
.onAppear {
|
||||||
|
// Launch-arg hooks for the screenshot/UI-test harness (same
|
||||||
|
// pattern as StationsView's `-showKeySheet`): `-tab <name>`
|
||||||
|
// opens the given tab; `-skipOnboarding` skips onboarding
|
||||||
|
// without touching the stored flag.
|
||||||
|
let args = ProcessInfo.processInfo.arguments
|
||||||
|
if let i = args.firstIndex(of: "-tab"), i + 1 < args.count {
|
||||||
|
switch args[i + 1] {
|
||||||
|
case "favourites": selectedTab = 1
|
||||||
|
case "alerts": selectedTab = 2
|
||||||
|
case "settings": selectedTab = 3
|
||||||
|
default: selectedTab = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
// Onboarding runs first on a fresh install — it owns the initial
|
// Onboarding runs first on a fresh install — it owns the initial
|
||||||
// permission prompts (location, notifications, and the data/local
|
// permission prompts (location, notifications, and the data/local
|
||||||
// network probe on the Data page). Location tracking and the first
|
// network probe on the Data page). Location tracking and the first
|
||||||
// network fetch start once it's done.
|
// network fetch start once it's done.
|
||||||
if FuelStore.loadHasCompletedOnboarding() {
|
// Launch-arg hook (UI-testing/screenshot harness, same pattern as
|
||||||
|
// StationsView's `-showKeySheet`): skip onboarding without
|
||||||
|
// touching the stored flag.
|
||||||
|
if FuelStore.loadHasCompletedOnboarding()
|
||||||
|
|| args.contains("-skipOnboarding") {
|
||||||
locationManager.startForegroundTracking()
|
locationManager.startForegroundTracking()
|
||||||
// Geofences must follow the user even in the background:
|
// Geofences must follow the user even in the background:
|
||||||
// wire the delegate hook (fires on every fix incl. background
|
// wire the delegate hook (fires on every fix incl. background
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>NSPrivacyTracking</key>
|
||||||
|
<false/>
|
||||||
|
<key>NSPrivacyTrackingDomains</key>
|
||||||
|
<array/>
|
||||||
|
<key>NSPrivacyCollectedDataTypes</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>NSPrivacyCollectedDataType</key>
|
||||||
|
<string>NSPrivacyCollectedDataTypeLocation</string>
|
||||||
|
<key>NSPrivacyCollectedDataTypeLinked</key>
|
||||||
|
<true/>
|
||||||
|
<key>NSPrivacyCollectedDataTypeTracking</key>
|
||||||
|
<false/>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>NSPrivacyCollectedDataType</key>
|
||||||
|
<string>NSPrivacyCollectedDataTypeDiagnostics</string>
|
||||||
|
<key>NSPrivacyCollectedDataTypeLinked</key>
|
||||||
|
<false/>
|
||||||
|
<key>NSPrivacyCollectedDataTypeTracking</key>
|
||||||
|
<false/>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
|
<key>NSPrivacyAccessedAPITypes</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>NSPrivacyAccessedAPIType</key>
|
||||||
|
<string>NSPrivacyAccessedAPICategoryUserDefaults</string>
|
||||||
|
<key>NSPrivacyAccessedAPITypeReasons</key>
|
||||||
|
<array>
|
||||||
|
<string>1C8F.1</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
@@ -229,7 +229,7 @@ struct SettingsView: View {
|
|||||||
} header: {
|
} header: {
|
||||||
Text("Data source")
|
Text("Data source")
|
||||||
} footer: {
|
} footer: {
|
||||||
Text("Connection shows whether prices come from the official Fuel Finder API or the CSV mirror. Data updated is the latest price change reported by the GOV.UK server itself.")
|
Text("Connection shows whether prices come from the official Fuel Finder API or the CSV mirror. Data updated is the latest price change reported by the GOV.UK server itself. Prices use the UK Fuel Finder dataset (Open Government Licence) and refresh up to twice a day.")
|
||||||
}
|
}
|
||||||
|
|
||||||
Section {
|
Section {
|
||||||
|
|||||||
@@ -101,8 +101,20 @@ struct StationsView: View {
|
|||||||
Text("Fetching prices…")
|
Text("Fetching prices…")
|
||||||
}
|
}
|
||||||
} else if stations.isEmpty {
|
} else if stations.isEmpty {
|
||||||
Text("No \(selectedFuel.displayName) stations found.")
|
let miles = distanceUnit.displayMiles(stationLimit)
|
||||||
.foregroundStyle(.secondary)
|
let unit = distanceUnit.label(for: Double(miles))
|
||||||
|
VStack(alignment: .leading, spacing: 6) {
|
||||||
|
Text("No \(selectedFuel.displayName) stations within \(miles) \(unit).")
|
||||||
|
.foregroundStyle(.secondary)
|
||||||
|
Text("FuelBoard covers England, Scotland and Wales — try Closest to browse every UK station.")
|
||||||
|
.font(.caption)
|
||||||
|
.foregroundStyle(.secondary)
|
||||||
|
Button("Browse all UK stations") {
|
||||||
|
sortMode = .closest
|
||||||
|
}
|
||||||
|
.font(.caption)
|
||||||
|
}
|
||||||
|
.padding(.vertical, 4)
|
||||||
} else {
|
} else {
|
||||||
ForEach(Array(stations.prefix(visibleCount)), id: \.id) { station in
|
ForEach(Array(stations.prefix(visibleCount)), id: \.id) { station in
|
||||||
StationRow(
|
StationRow(
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>NSPrivacyTracking</key>
|
||||||
|
<false/>
|
||||||
|
<key>NSPrivacyTrackingDomains</key>
|
||||||
|
<array/>
|
||||||
|
<key>NSPrivacyCollectedDataTypes</key>
|
||||||
|
<array/>
|
||||||
|
<key>NSPrivacyAccessedAPITypes</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>NSPrivacyAccessedAPIType</key>
|
||||||
|
<string>NSPrivacyAccessedAPICategoryUserDefaults</string>
|
||||||
|
<key>NSPrivacyAccessedAPITypeReasons</key>
|
||||||
|
<array>
|
||||||
|
<string>1C8F.1</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>logFormatVersion</key>
|
||||||
|
<integer>12</integer>
|
||||||
|
<key>logs</key>
|
||||||
|
<dict/>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>logFormatVersion</key>
|
||||||
|
<integer>12</integer>
|
||||||
|
<key>logs</key>
|
||||||
|
<dict/>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>logFormatVersion</key>
|
||||||
|
<integer>12</integer>
|
||||||
|
<key>logs</key>
|
||||||
|
<dict/>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>logFormatVersion</key>
|
||||||
|
<integer>12</integer>
|
||||||
|
<key>logs</key>
|
||||||
|
<dict/>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user