Compare commits
@@ -1,5 +1,8 @@
|
|||||||
build/
|
build/
|
||||||
build-release/
|
build-release/
|
||||||
|
build-sim/
|
||||||
|
build-watch/
|
||||||
.DS_Store
|
.DS_Store
|
||||||
*.xcuserstate
|
*.xcuserstate
|
||||||
xcuserdata/
|
xcuserdata/
|
||||||
|
FuelBoardTests/.build/
|
||||||
|
|||||||
@@ -0,0 +1,129 @@
|
|||||||
|
# FuelBoard App Store Submission Plan
|
||||||
|
|
||||||
|
Updated: 2026-08-16 · Target: safe App Store submission.
|
||||||
|
Companion to SECURITY.md (code security) — this file is the *review-gate*
|
||||||
|
checklist: what must happen, who owns it, and the timeline.
|
||||||
|
|
||||||
|
## Status: SUBMITTABLE after the checklist below
|
||||||
|
|
||||||
|
The two 2026-08-11 blockers are RESOLVED by P0 (live chain):
|
||||||
|
- ~~LAN relay data source~~ → **app fetches from public HTTPS
|
||||||
|
(raw.githubusercontent.com mirror) with a bundled real 8,022-station dump
|
||||||
|
as the no-network fallback.** No private IP in the consumer path. Reviewers
|
||||||
|
on Apple's network see real data.
|
||||||
|
- ~~Always-location risk~~ → **Always is requested only when the user enables
|
||||||
|
Alerts** (ProximityMonitor escalates WhenInUse → Always on alert opt-in).
|
||||||
|
Onboarding requests WhenInUse only.
|
||||||
|
|
||||||
|
## Hard blockers (must happen before ANY submission)
|
||||||
|
|
||||||
|
1. **[USER] Apple Developer Program membership** — the app has never been
|
||||||
|
signed (all builds are `CODE_SIGNING_ALLOWED=NO` + ad-hoc re-sign for
|
||||||
|
SideStore). Requires a paid membership ($99/yr, up to 48 h to approve) and
|
||||||
|
a developer account.
|
||||||
|
2. **[USER] Register App ID `com.apt.fuelboard`** in the developer portal,
|
||||||
|
with the **App Group `group.com.apt.fuelboard`** capability AND the
|
||||||
|
**In-App Purchase** capability enabled (the widget + app-group cache
|
||||||
|
depend on the group; the tip jar needs IAP).
|
||||||
|
3. ~~[ME] `PrivacyInfo.xcprivacy`~~ — **DONE 2026-08-16** (app + widget
|
||||||
|
manifests added; UserDefaults `1C8F.1`, Location + Diagnostics declared).
|
||||||
|
4. **[ME] Real Distribution signing** — Distribution certificate + App Store
|
||||||
|
provisioning profile (or Xcode automatic signing once the account is in
|
||||||
|
Xcode); then `xcodebuild archive` (no more `CODE_SIGNING_ALLOWED=NO`).
|
||||||
|
5. **[USER] Privacy policy URL** in App Store Connect — mandatory; the app
|
||||||
|
collects location. A one-page GitHub Pages privacy policy is fine.
|
||||||
|
|
||||||
|
## Required tasks — owner [ME] (technical, do after account exists)
|
||||||
|
|
||||||
|
- [x] **PrivacyInfo.xcprivacy** — app + widget manifests added (2026-08-16):
|
||||||
|
UserDefaults reason `1C8F.1`; app also declares Location (linked, not
|
||||||
|
tracking) + Diagnostics (not linked, not tracking — the dev beacon)
|
||||||
|
- [ ] Distribution archive + TestFlight upload (needs Apple account)
|
||||||
|
- [x] **Version bump** → `1.0.1` (build 2), app + widget plists
|
||||||
|
- [x] **OGL attribution** — Settings → Data source footer now reads "Prices
|
||||||
|
use the UK Fuel Finder dataset (Open Government Licence) and refresh
|
||||||
|
up to twice a day." (2.3.1 hygiene)
|
||||||
|
- [x] **Out-of-UK empty state** — "No X stations within N miles" + UK-only
|
||||||
|
explainer + "Browse all UK stations" button (switches to Closest)
|
||||||
|
- [ ] ~~Gate dev surface behind #if DEBUG~~ **DECLINED — kept in Release on
|
||||||
|
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
|
||||||
|
- [ ] ~~Capture screenshots from simulator~~ **RAW CAPTURES DONE (2026-08-16)** —
|
||||||
|
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)
|
||||||
|
|
||||||
|
- [ ] Apple Developer Program membership + App ID (blockers #1–#2)
|
||||||
|
- [ ] **Tip jar: create the three consumable IAPs** in App Store Connect →
|
||||||
|
In-App Purchases (all type **Consumable**, exact product IDs, UK price
|
||||||
|
tier; tip jars are an accepted App Review pattern):
|
||||||
|
- `com.apt.fuelboard.tip099` — **£0.99** — "Splash & Dash" (Just enough to keep things moving.)
|
||||||
|
- `com.apt.fuelboard.tip299` — **£2.99** — "Half a Tank" (A generous top-up for development.)
|
||||||
|
- `com.apt.fuelboard.tip499` — **£4.99** — "Fill 'Er Up" (Keeping the app on the road.)
|
||||||
|
App-side is ready (`41e4c91`): Settings → Support FuelBoard shows all
|
||||||
|
three tiers with per-tier prices; sideloaded builds gracefully show
|
||||||
|
"not available in this build yet" until the products exist.
|
||||||
|
- [ ] App Store Connect app record: name, bundle ID `com.apt.fuelboard`,
|
||||||
|
category (Utilities), pricing (Free), availability (UK first?)
|
||||||
|
- [ ] Description, keywords, support URL, marketing URL, privacy policy URL
|
||||||
|
- [ ] Privacy nutrition labels questionnaire (location: yes, linked to user
|
||||||
|
for alerts/nearby; diagnostics: yes — the widget-diag beacon, not
|
||||||
|
linked, not used for tracking; no ATT prompt needed)
|
||||||
|
- [ ] Age rating questionnaire (4+; no objectionable content)
|
||||||
|
- [ ] Export compliance: HTTPS-only + keychain → standard encryption
|
||||||
|
exemption (5A992a), no documentation needed
|
||||||
|
- [ ] Approve the TestFlight build and do a real-device pass before submit
|
||||||
|
- [ ] Final "Submit for Review" click + stand by to answer review questions
|
||||||
|
|
||||||
|
## Review notes (copy-paste into App Store Connect)
|
||||||
|
|
||||||
|
> FuelBoard shows the cheapest petrol, diesel, and premium fuel near the user
|
||||||
|
> using the UK government Fuel Finder dataset (Open Government Licence).
|
||||||
|
> Prices are downloaded from the internet up to twice a day; the app filters
|
||||||
|
> 5/10/15-mile radii on-device and works offline from a bundled snapshot.
|
||||||
|
> Location is used to (a) rank stations by distance and (b) power the
|
||||||
|
> optional Alerts feature: when the user enables alerts, the app geofences
|
||||||
|
> the cheapest station in their radius and sends a notification when they
|
||||||
|
> approach it. Always-location is requested only when the user enables
|
||||||
|
> alerts. The app also provides a home-screen widget showing the current
|
||||||
|
> cheapest station nearby, and a price-history Trends chart for starred
|
||||||
|
> stations. All data is UK-wide (Fuel Finder covers England, Scotland, Wales
|
||||||
|
> and Northern Ireland); users outside the UK see an empty state explaining
|
||||||
|
> this.
|
||||||
|
|
||||||
|
## Review risks (know before submitting)
|
||||||
|
|
||||||
|
- **Always-location (5.1.1)** — the #1 question. The feature is visible
|
||||||
|
(Alerts tab) and opt-in; the review notes above say so explicitly.
|
||||||
|
- **Reviewer outside the UK** — empty radius; mitigated by the empty-state
|
||||||
|
task above + review notes.
|
||||||
|
- **"Cheapest" claims (2.3.1)** — mitigated by the OGL/footnote task.
|
||||||
|
- **Live Activities** — reviewers may ask why; answer: price alerts at a
|
||||||
|
glance. It is declared in the plist already.
|
||||||
|
- **Brand logos (Shell, BP, Tesco…)** — Simple Icons (CC0) + Wikimedia,
|
||||||
|
nominative use (cf. GasBuddy); low risk, keep sources documented.
|
||||||
|
- **GitHub raw as data source** — public data, no credentials; bundled dump
|
||||||
|
covers any regional GitHub unreachability.
|
||||||
|
|
||||||
|
## Timeline (best case)
|
||||||
|
|
||||||
|
| Day | Owner | Milestone |
|
||||||
|
|-----|-------|-----------|
|
||||||
|
| 0–2 | USER | Developer Program enrollment + App ID + app group capability |
|
||||||
|
| 2–3 | ME | Signing (automatic), PrivacyInfo.xcprivacy, attribution, empty state, version bump |
|
||||||
|
| 3–4 | ME | Archive → TestFlight; screenshots captured |
|
||||||
|
| 4–5 | USER | TestFlight install + real-device pass (onboarding, alerts, widget, offline) |
|
||||||
|
| 5–6 | USER | App Store Connect metadata + labels + age rating + privacy URL |
|
||||||
|
| 6–7 | USER/ME | Submit for review |
|
||||||
|
| 7–14 | Apple | Review (typically 1–3 days, can be a week); answer questions same-day |
|
||||||
|
|
||||||
|
Rollback note: nothing about submission changes the repo's rollback posture
|
||||||
|
(`main` history is intact; release builds are tagged per submission).
|
||||||
|
After Width: | Height: | Size: 273 KiB |
|
After Width: | Height: | Size: 841 KiB |
|
After Width: | Height: | Size: 306 KiB |
|
After Width: | Height: | Size: 354 KiB |
|
After Width: | Height: | Size: 278 KiB |
|
After Width: | Height: | Size: 444 KiB |
|
After Width: | Height: | Size: 266 KiB |
|
After Width: | Height: | Size: 346 KiB |
|
After Width: | Height: | Size: 268 KiB |
|
After Width: | Height: | Size: 815 KiB |
@@ -0,0 +1,248 @@
|
|||||||
|
# FuelBoard Backlog
|
||||||
|
|
||||||
|
FuelBoard — UK-wide petrol-station price widget + app.
|
||||||
|
GitHub mirror (`aptonline/fuelboard-data`, 2×/day 10:00+16:00) holds the
|
||||||
|
full-UK dump (~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.
|
||||||
|
Status: TODO / IN PROGRESS / DONE / BLOCKED.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## P1 — Soon
|
||||||
|
|
||||||
|
- [x] **Move relay + mirror off the Mac mini (DONE 2026-08-16)** — dedicated
|
||||||
|
Debian 13 LXC on Proxmox via community-scripts/ProxmoxVE `ct/debian.sh`
|
||||||
|
(CT **111**, hostname **`fuelboard-relay`**, 1 core/512 MB/2 GB, static IP
|
||||||
|
**192.168.1.113**, plain LXC — NO Docker layer). Scope: relay AND mirror
|
||||||
|
both moved (full Mac independence; relays bind **loopback only**, GitHub is
|
||||||
|
the only public surface). What landed: Python 3.13.5 (trixie) boots both
|
||||||
|
relays (relay 8788 demo/CSV + relay-api 8789 `source: api`, 8,024 stations,
|
||||||
|
~2 min first sync, 0 failures); source at `/root/workspace/fuelboard-{relay,relay-api}`
|
||||||
|
(venvs + pip, `.env` chmod 600); deploy key `fuelboard_deploy` +
|
||||||
|
`github-fuelboard-data` ssh config + `fuelboard-data` clone at
|
||||||
|
`/root/workspace/fuelboard-data` (origin = ssh alias, green + synced);
|
||||||
|
`mirror_push.py` → `/root/scripts/mirror_push.py` (RELAY line already
|
||||||
|
`127.0.0.1:8789`); systemd `fuelboard-relay.service` +
|
||||||
|
`fuelboard-relay-api.service` (Restart=always, `--host 127.0.0.1`) +
|
||||||
|
`fuelboard-mirror.service` (oneshot) + `.timer` (10:00 + 16:00, Persistent,
|
||||||
|
armed — next Mon 10:00); first live push VERIFIED from outside
|
||||||
|
(commit `735a292`, latest.json `data_updated 16:50:53Z`); Mac launchd
|
||||||
|
`com.apt.fuelboard-{relay,relay-api,mirror}` unloaded + plists parked in
|
||||||
|
`~/Library/LaunchAgents/fuelboard-migrated-to-ct/`; editor 8790 + IPA 8765
|
||||||
|
stay on the Mac; price watchdog `relay_price_watchdog.py` now fetches via
|
||||||
|
SSH (`~/.ssh/fuelboard_ct` → `/root/scripts/watchdog_fetch.py`). Logs via
|
||||||
|
`journalctl -u fuelboard-*` on the CT; SSH into CT: `ssh -i ~/.ssh/fuelboard_ct root@192.168.1.113`.
|
||||||
|
**Open: rootfs is 2 GB (1.1 GB free) — mirror retention ~940 MB will get
|
||||||
|
tight; rootfs grown to 8 GB on pve 2026-08-16 (5.9 GB free).**
|
||||||
|
|
||||||
|
- [x] **Siri: "Cheapest [fuel] near me"** — `AppShortcutsProvider` + `CheapestFuelIntent`
|
||||||
|
(App Intents, iOS 16+), dialog + price-card snippet, cached full-UK dump first
|
||||||
|
with freshness label ("as of 6am"), last-known-location fallback. Constraint:
|
||||||
|
fresh-data path needs a reachable source off-LAN — cached answers work anywhere
|
||||||
|
the app has previously fetched (the GitHub mirror serves this now). *DONE —
|
||||||
|
radius-scoped
|
||||||
|
lookup `037c241` (real-data regression: GULF HISTON 100.9p 132 mi away),
|
||||||
|
per-fuel literal-word shortcuts `e55078c`, names = app words `a3b6f37`, plus
|
||||||
|
Directions-to-cheapest `caadf18` + spoken hand-off `aed99e3`.*
|
||||||
|
- [ ] **Siri: "Closest [fuel] station"** — same plumbing as cheapest, sort by
|
||||||
|
distance instead of price. Free second phrase in the same `AppShortcutsProvider`.
|
||||||
|
- [x] **Favourites Trends graph** — price history chart for starred stations.
|
||||||
|
Swift Charts (iOS 26 target) line chart in the Favourites tab; one series per
|
||||||
|
favourite, fuel-scoped (station, fuel) pairs map 1:1 to the archive. Default =
|
||||||
|
absolute price lines; toggle "vs cheapest favourite" re-baselines to 0 as signed
|
||||||
|
pence (mirrors the Cheapest/Closest delta pattern users already know). Reuse the
|
||||||
|
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
|
||||||
|
each refresh — check back in a few days"). No widget in v1 (one-kind rule).
|
||||||
|
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
|
||||||
|
band, series + deltaSeries, app-group favourites-only cache, parallel per-day
|
||||||
|
fetches) + `FuelBoard/TrendsView.swift` (chart, fuel capsule, 7/30/90, Price/vs
|
||||||
|
cheapest, per-station legend, empty/loading/retry states) + toolbar entry in
|
||||||
|
FavouritesView + strings. 83 tests incl. URL regression (appendingPathComponent
|
||||||
|
— URL(string:relativeTo:) dropped /main, all fetches 404'd). Sim-verified:
|
||||||
|
favourites rows + TOP/deltas; sheet controls + honest empty state w/ live first-
|
||||||
|
snapshot date. Pending: lines render once ≥2 snapshots (archive warming daily,
|
||||||
|
~Aug 17).*
|
||||||
|
|
||||||
|
## P2 — Later
|
||||||
|
|
||||||
|
- [ ] **Shortcuts: station price lookup** — `StationEntity` + `EntityQuery` so
|
||||||
|
Shortcuts/Siri pick a station by name; `ReturnsValue<Double>` enables user
|
||||||
|
automations ("get cheapest diesel → notify").
|
||||||
|
- [x] **Siri: favourite station price** — "How much is my favourite unleaded?"
|
||||||
|
`FavouriteFuelPriceIntent`: TOP favourite per fuel (first in the manual
|
||||||
|
Favourites order — `SiriCheapestLookup.topFavourite`, refresh-vs-dump with
|
||||||
|
offline cached-snapshot fallback); no location required (distance shown only
|
||||||
|
when the saved fix exists). `DirectionsToFavouriteFuelIntent` (Maps
|
||||||
|
hand-off, station announced first) stays in code but carries no voice
|
||||||
|
phrases — every favourite price card has the interactive Directions button
|
||||||
|
(`6d7254e`), and the voice slots went to Directions-to-Cheapest (`8a4c472`).
|
||||||
|
*DONE — favourites already carried station snapshots, so no `StationEntity`
|
||||||
|
dependency.*
|
||||||
|
- [ ] **Siri: on-screen awareness (View Annotations + StationEntity)** — iOS
|
||||||
|
26/27 Siri prefers the visible screen over the App Shortcut when the app is
|
||||||
|
frontmost; without view annotations it answers from the rendered screen
|
||||||
|
(unstructured "screenshot" parse) — visible tab/sort/radius, not the full
|
||||||
|
dataset or the intent's logic. Fix: `StationEntity` (shared with the station
|
||||||
|
lookup P2 above) + `.appEntityIdentifier` on station rows (collection
|
||||||
|
annotation on the List) + `NSUserActivity` on the station detail/map +
|
||||||
|
App Schema conformance (optional `IndexedEntity` for Spotlight semantic
|
||||||
|
search). Verify annotations with AppIntentsTesting (P3). Off-screen/locked
|
||||||
|
requests already route to `CheapestFuelIntent` correctly; this closes the
|
||||||
|
frontmost gap. WWDC26: "Build intelligent Siri experiences with App Schemas"
|
||||||
|
+ "Explore advanced App Intents features".
|
||||||
|
- [ ] **Lock screen widgets** — new `FuelPriceLockScreen` kind (fuel-only config
|
||||||
|
intent, reuses `FuelPriceTimelineProvider`): `accessoryRectangular` (cheapest
|
||||||
|
station + price + distance), `accessoryCircular` (price ring), `accessoryInline`
|
||||||
|
("⛽ 139.9p"). Provider + `containerBackground` already exist; must branch the
|
||||||
|
view for accessory families (current `else` renders `stationList`). Bonus:
|
||||||
|
StandBy mode. ~1 session.
|
||||||
|
|
||||||
|
- [ ] **CarPlay** — widgets render but can't launch Maps; consider a proper CarPlay
|
||||||
|
scene (CPListTemplate) for navigation deep-links.
|
||||||
|
- [ ] **Alert region tuning** — make alert trigger radius + dedup window
|
||||||
|
user-configurable (currently fixed 300 m trigger / 1 per station per hour).
|
||||||
|
- [ ] **Price-drop alerts: threshold control + Check now** — keep the current
|
||||||
|
cache-first / 12 h refresh policy, but add a per-alert sensitivity control
|
||||||
|
for favourite price-drop alerts instead of a fetch-frequency slider. Scope:
|
||||||
|
`Minimum change` picker (e.g. 0.5p / 1.0p / 2.0p), plus a `Check now`
|
||||||
|
action in the Alerts tab that forces a fetch and immediately re-evaluates the
|
||||||
|
cheapest-favourite alert. Copy should set expectation honestly: standard
|
||||||
|
mode checks when FuelBoard refreshes prices (up to twice daily).
|
||||||
|
- [ ] **Price-drop alerts: optional frequent-check mode** — if the app ever
|
||||||
|
relaxes the current "twice daily max" data policy, expose this as a clearly
|
||||||
|
separate mode rather than a freeform frequency slider. Example framing:
|
||||||
|
`Standard` (current behaviour) vs `Frequent` (best-effort extra checks),
|
||||||
|
with conservative caps (e.g. every 4–6 h, not hourly) and copy that does not
|
||||||
|
promise exact timing under iOS background scheduling.
|
||||||
|
- [ ] **Pull-to-refresh spinner state** — surface refresh in-flight state
|
||||||
|
(currently `refreshable` fires but no visible progress in the row list).
|
||||||
|
- [x] **Offline first-run** — bundled REAL 8,022-station dump (`FuelBoardDump`,
|
||||||
|
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.
|
||||||
|
- [ ] **Dark mode polish** — verify brand logos (white-circle clips) and RAG dot
|
||||||
|
contrast under dark appearance on-device.
|
||||||
|
- [ ] **Trends: national average overlay + per-day min/max** — the full dump is
|
||||||
|
already held, so the UK/regional average line is computable locally; archive can
|
||||||
|
store per-day min/max so the chart shows intraday spikes, not just the daily point.
|
||||||
|
|
||||||
|
## P3 — Ideas / Research
|
||||||
|
|
||||||
|
- [ ] **Siri: create fuel alert** — voice-create an alert for a station. Deferred:
|
||||||
|
alert semantics (cheapest-within-radius + hourly dedup) are hard to express in
|
||||||
|
a dialog; needs the alert engine surfaced as an intent.
|
||||||
|
- [ ] **App Intents testing** — AppIntentsTesting (iOS 27) for intent coverage
|
||||||
|
instead of XCUITest when the Siri features land.
|
||||||
|
|
||||||
|
- [ ] **Fuel-price prediction** — "price tends up/down" arrow derived from the
|
||||||
|
price-history archive (P0). Cheap once the archive + Trends graph exist; keep a
|
||||||
|
separate phase. Backlog ancestry: this was the original "relay could keep a
|
||||||
|
rolling archive" idea — the archive is now P0 and the Trends graph (P1) is its
|
||||||
|
visible half.
|
||||||
|
- [ ] **Foresight Live Activity (heading-aware next station)** — investigate a
|
||||||
|
driving-time surface that predicts the likely next station ahead of the user
|
||||||
|
from cached data only (no extra source-data refresh on motion). Best v1 shape:
|
||||||
|
Live Activity option, not widget-first, because widget space/refresh budgets
|
||||||
|
are tight and the lock-screen/CarPlay compact slot better matches the use case.
|
||||||
|
Prefer ONE FuelBoard Live Activity with a mode selector (e.g. `Nearby cheapest`
|
||||||
|
vs `Foresight / next ahead`) rather than trying to run two simultaneous
|
||||||
|
FuelBoard activities; iOS can support multiple activities, but FuelBoard's
|
||||||
|
current implementation is single-activity and the compact surfaces are too
|
||||||
|
constrained for two competing driving modes anyway. Viable MVP: infer heading
|
||||||
|
from recent motion/course, keep only stations in a forward cone, score
|
||||||
|
candidates by forward distance + lateral offset, and show the next-ahead
|
||||||
|
station with all three fuel prices or a compact rotating/focused fuel
|
||||||
|
treatment if space allows. Key caveat: heading is not full route awareness,
|
||||||
|
so avoid overpromising "the next station you will reach" on motorways/opposite
|
||||||
|
carriageways; frame as "next ahead" / "likely next stop" unless route-quality
|
||||||
|
heuristics improve. Must include hysteresis/speed thresholds to avoid flapping,
|
||||||
|
and should be tested specifically against compact Live Activity/CarPlay layouts
|
||||||
|
where space is extremely limited.
|
||||||
|
- [ ] **Widget sparkline** — medium-widget mini sparkline of a favourite's 30-day
|
||||||
|
trend, after the Trends UI lands (one-kind rule preserved).
|
||||||
|
- [ ] **Price alerts for a specific station** — notify when a starred station's
|
||||||
|
price drops below a user threshold.
|
||||||
|
- [ ] **Price-drop alerts: mode = Cheapest favourite / All favourites** — keep
|
||||||
|
`Cheapest favourite` as the default low-noise mode, but explore an optional
|
||||||
|
`All favourites` mode for one fuel. Guardrails if built: threshold required,
|
||||||
|
per-station dedupe based on last-alerted price (not just time), and combine
|
||||||
|
multiple drops from one refresh into a single summary notification rather
|
||||||
|
than one alert per station.
|
||||||
|
- [ ] **Share sheet** — share "Cheapest Unleaded near me" as text/image card.
|
||||||
|
- [ ] **Watch app v1 — favourites + Check now** — small Apple Watch companion,
|
||||||
|
not a phone-app clone. Favourites-first home screen: fuel picker, `Cheapest
|
||||||
|
favourite` summary card, top few favourites for that fuel, last-updated time,
|
||||||
|
and a prominent `Check now` action that forces a refresh + re-evaluates
|
||||||
|
favourite prices. Keep the watch UX pull-based and glanceable; avoid implying
|
||||||
|
high-frequency background refresh. Shared-store architecture preferred over a
|
||||||
|
second watch-only data model.
|
||||||
|
- [ ] **Watch complications / Smart Stack** — complication/widget surfaces for
|
||||||
|
the same watch concept: show the cheapest favourite price, fuel, and short
|
||||||
|
station name at a glance. Keep copy minimal and honest about freshness.
|
||||||
|
- [ ] **Watch app v2 — favourite detail + handoff** — tap a favourite row for a
|
||||||
|
detail screen (station, brand, price, last updated) with actions like
|
||||||
|
`Directions` and `Open on iPhone`. Reuse existing maps handoff patterns where
|
||||||
|
possible instead of inventing watch-only navigation logic.
|
||||||
|
- [ ] **App Clip** — 10 MB budget for "find cheapest fuel nearby" without install.
|
||||||
|
- [ ] **Widget picker: dual-unit distance labels** — the Edit-Widget Distance row
|
||||||
|
label is cached by the system at sheet-render time and only re-resolves on
|
||||||
|
picker interaction, so "mirror the app unit" goes stale. Fix: label options
|
||||||
|
with both units ("5 miles (8 km)") so nothing can go stale; widget rows keep
|
||||||
|
mirroring the live unit.
|
||||||
|
- [ ] **Localizable.strings: normalize keys to match copy** — today 8 onboarding
|
||||||
|
keys diverge from their values (key = old source literal, value = tightened
|
||||||
|
copy, e.g. "…updated twice a day" key vs "…updated daily" value). Option to
|
||||||
|
rename keys so key == value everywhere. Requires sweeping every Swift
|
||||||
|
occurrence of the old literal (Text/NSLocalizedString) AND updating key +
|
||||||
|
value together in one commit + rebuild — renaming the file entry alone
|
||||||
|
silently reverts copy to the Swift literal at next build. Not recommended:
|
||||||
|
keys are invisible anchors; value edits already change copy without code
|
||||||
|
changes, and normalization reintroduces divergence on the next value edit.
|
||||||
|
|
||||||
|
## Done (recent)
|
||||||
|
|
||||||
|
- [x] **GitHub price-history mirror + LIVE provider chain (P0, DONE 2026-08-16)** —
|
||||||
|
push side (relay → public `aptonline/fuelboard-data`, daily 10:00 launchd, SSH
|
||||||
|
deploy key, 180-day retention) + history read side (Trends, P1) + **live chain
|
||||||
|
(`feature/p0-live-chain`)**: `MirrorFuelProvider` fetches CURRENT prices from
|
||||||
|
raw.githubusercontent (latest.json → history/<day>.json, app-group day-cache) →
|
||||||
|
**GitHub FIRST everywhere** (alert checks + widget too — the relay is
|
||||||
|
unreachable from any consumer phone and attempting it would fire the Local
|
||||||
|
Network prompt); LAN relay joins ONLY behind the hidden dev flag
|
||||||
|
`fuelboard.relayFallback` (Settings → Debug, off by default); bundled REAL
|
||||||
|
dump (`FuelBoardDump` dataset, refreshed by `scripts/refresh_bundled_dump.sh`)
|
||||||
|
is the app's no-network last resort. `FuelBeacon` fire-and-forget X-Client app
|
||||||
|
ping fires only with the dev flag on. **Local Network permission removed from
|
||||||
|
onboarding entirely** — data page is informational, replay on cellular can no
|
||||||
|
longer hang. 95 tests.
|
||||||
|
- [x] Switch dataset to govuk API data — official Fuel Finder OAuth API via
|
||||||
|
relay-api :8789 (source: api, ~8,012 stations); app baseURL → :8789; credentials
|
||||||
|
live in relay `.env` only, never in the IPA
|
||||||
|
- [x] Widget configuration — per-widget fuel/sort/distance via
|
||||||
|
`WidgetConfigurationIntent` (`AppIntentConfiguration`), distance options as
|
||||||
|
`WidgetDistance` AppEntity (`26e724f`); small widget gained Favourites
|
||||||
|
(`d0bd4e7`) + `.systemMedium` (`b94f769`)
|
||||||
|
- [x] Unit test target for the app — FuelBoardShared XCTest target, 37 tests
|
||||||
|
(title sanitizer, decode price guard, RAG thresholds, cheapest/closest baseline,
|
||||||
|
miles↔km)
|
||||||
|
- [x] Backlog hygiene — implemented items marked DONE, obsolete pruned
|
||||||
|
- [x] Sync retry with exponential backoff — relay-api retries failed syncs
|
||||||
|
15s→15min cap; `/health` exposes retry state (`6a58275`)
|
||||||
|
- [x] England-wide full-UK dump, on-device filtering (relay `142061b`, app `cf9765b`)
|
||||||
|
- [x] Cache-first, 12 h gate, pull-to-refresh only
|
||||||
|
- [x] Distance filter 5/10/15 miles, local-only changes (no re-fetch)
|
||||||
|
- [x] Closest mode = whole country, distance picker disabled
|
||||||
|
- [x] Delta/TOP baseline = cheapest-within-radius in both modes
|
||||||
|
- [x] Three-layer price sanitisation (relay band 50–500p / app decode guard / title-case)
|
||||||
|
- [x] Title-case station names (chunk-aware sanitizer)
|
||||||
|
- [x] Native large-title navigation (Mail-style collapse) — `313fe84`
|
||||||
|
- [x] E10/E5 bracket labels in picker + descriptions — `05f3ef0`
|
||||||
|
- [x] Gitea remotes created + pushed (`apt/fuelboard`, `apt/fuelboard-relay`)
|
||||||
|
- [x] Brand logos: proper 2x/3x assets, 75% smaller (315 KB → 79 KB)
|
||||||
|
- [x] Security audit (see SECURITY.md)
|
||||||
|
- [x] App Store approval gotcha review (see APPSTORE.md)
|
||||||
@@ -17,17 +17,37 @@
|
|||||||
<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>CFBundleURLTypes</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>CFBundleURLName</key>
|
||||||
|
<string>com.apt.fuelboard</string>
|
||||||
|
<key>CFBundleURLSchemes</key>
|
||||||
|
<array>
|
||||||
|
<string>fuelboard</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>1</string>
|
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||||
<key>LSRequiresIPhoneOS</key>
|
<key>LSRequiresIPhoneOS</key>
|
||||||
<true/>
|
<true/>
|
||||||
<key>NSLocationWhenInUseUsageDescription</key>
|
<key>NSLocalNetworkUsageDescription</key>
|
||||||
<string>FuelBoard uses your location to find the cheapest nearby petrol stations.</string>
|
<string>FuelBoard can optionally connect to the FuelBoard Relay on your local network for faster alerts during development.</string>
|
||||||
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
|
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
|
||||||
<string>FuelBoard uses Always location to alert you when you approach the cheapest station nearby.</string>
|
<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>
|
||||||
|
<true/>
|
||||||
|
<key>BGTaskSchedulerPermittedIdentifiers</key>
|
||||||
|
<array>
|
||||||
|
<string>com.apt.fuelboard.smart-refresh</string>
|
||||||
|
</array>
|
||||||
<key>UIBackgroundModes</key>
|
<key>UIBackgroundModes</key>
|
||||||
<array>
|
<array>
|
||||||
|
<string>fetch</string>
|
||||||
<string>location</string>
|
<string>location</string>
|
||||||
</array>
|
</array>
|
||||||
<key>UILaunchScreen</key>
|
<key>UILaunchScreen</key>
|
||||||
|
|||||||
@@ -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>com.apple.security.application-groups</key>
|
||||||
|
<array>
|
||||||
|
<string>group.com.apt.fuelboard</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
<?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>CFBundleDevelopmentRegion</key>
|
||||||
|
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||||
|
<key>CFBundleDisplayName</key>
|
||||||
|
<string>FuelBoardWatch</string>
|
||||||
|
<key>CFBundleExecutable</key>
|
||||||
|
<string>$(EXECUTABLE_NAME)</string>
|
||||||
|
<key>CFBundleIdentifier</key>
|
||||||
|
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||||
|
<key>CFBundleInfoDictionaryVersion</key>
|
||||||
|
<string>6.0</string>
|
||||||
|
<key>CFBundleName</key>
|
||||||
|
<string>$(PRODUCT_NAME)</string>
|
||||||
|
<key>CFBundlePackageType</key>
|
||||||
|
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
|
||||||
|
<key>CFBundleShortVersionString</key>
|
||||||
|
<string>$(MARKETING_VERSION)</string>
|
||||||
|
<key>CFBundleVersion</key>
|
||||||
|
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||||
|
<key>NSExtension</key>
|
||||||
|
<dict>
|
||||||
|
<key>NSExtensionAttributes</key>
|
||||||
|
<dict>
|
||||||
|
<key>WKAppBundleIdentifier</key>
|
||||||
|
<string>com.apt.fuelboard.watchapp</string>
|
||||||
|
</dict>
|
||||||
|
<key>NSExtensionPointIdentifier</key>
|
||||||
|
<string>com.apple.watchkit</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
<?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>CFBundleDevelopmentRegion</key>
|
||||||
|
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||||
|
<key>CFBundleDisplayName</key>
|
||||||
|
<string>FuelBoard</string>
|
||||||
|
<key>CFBundleExecutable</key>
|
||||||
|
<string>$(EXECUTABLE_NAME)</string>
|
||||||
|
<key>CFBundleIdentifier</key>
|
||||||
|
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||||
|
<key>CFBundleInfoDictionaryVersion</key>
|
||||||
|
<string>6.0</string>
|
||||||
|
<key>CFBundleName</key>
|
||||||
|
<string>FuelBoard</string>
|
||||||
|
<key>CFBundlePackageType</key>
|
||||||
|
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
|
||||||
|
<key>CFBundleShortVersionString</key>
|
||||||
|
<string>$(MARKETING_VERSION)</string>
|
||||||
|
<key>CFBundleVersion</key>
|
||||||
|
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||||
|
<key>UISupportedInterfaceOrientations</key>
|
||||||
|
<array>
|
||||||
|
<string>UIInterfaceOrientationPortrait</string>
|
||||||
|
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||||
|
</array>
|
||||||
|
<key>WKCompanionAppBundleIdentifier</key>
|
||||||
|
<string>com.apt.fuelboard</string>
|
||||||
|
<key>WKWatchKitApp</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
@@ -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>$(MARKETING_VERSION)</string>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>1</string>
|
<string>$(CURRENT_PROJECT_VERSION)</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>
|
||||||
|
|||||||
|
After Width: | Height: | Size: 73 KiB |
|
After Width: | Height: | Size: 223 KiB |
|
After Width: | Height: | Size: 1.4 MiB |
@@ -0,0 +1,69 @@
|
|||||||
|
{
|
||||||
|
"fill-specializations" : [
|
||||||
|
{
|
||||||
|
"value" : {
|
||||||
|
"automatic-gradient" : "srgb:1.00000,1.00000,1.00000,1.00000"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"appearance" : "light",
|
||||||
|
"value" : {
|
||||||
|
"automatic-gradient" : "srgb:1.00000,1.00000,1.00000,1.00000"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"groups" : [
|
||||||
|
{
|
||||||
|
"layers" : [
|
||||||
|
{
|
||||||
|
"glass" : false,
|
||||||
|
"hidden" : false,
|
||||||
|
"image-name" : "0-currency_pound-2.png",
|
||||||
|
"name" : "currency_pound 2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"glass" : false,
|
||||||
|
"hidden" : false,
|
||||||
|
"image-name" : "1-water_drop.png",
|
||||||
|
"name" : "water_drop"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name" : "Layers",
|
||||||
|
"shadow" : {
|
||||||
|
"kind" : "none",
|
||||||
|
"opacity" : 0.5
|
||||||
|
},
|
||||||
|
"specular" : true,
|
||||||
|
"translucency" : {
|
||||||
|
"enabled" : false,
|
||||||
|
"value" : 0.5
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"layers" : [
|
||||||
|
{
|
||||||
|
"glass" : false,
|
||||||
|
"hidden" : false,
|
||||||
|
"image-name" : "Background.png",
|
||||||
|
"name" : "Background"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name" : "Background",
|
||||||
|
"shadow" : {
|
||||||
|
"kind" : "none",
|
||||||
|
"opacity" : 0.5
|
||||||
|
},
|
||||||
|
"specular" : false,
|
||||||
|
"translucency" : {
|
||||||
|
"enabled" : false,
|
||||||
|
"value" : 0.5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"supported-platforms" : {
|
||||||
|
"circles" : [
|
||||||
|
"watchOS"
|
||||||
|
],
|
||||||
|
"squares" : "shared"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 1.2 MiB |
@@ -1,7 +1,4 @@
|
|||||||
// !$*UTF8*$!
|
// !$*UTF8*$!
|
||||||
// FuelBoard — hand-crafted project (objectVersion 77, synchronized folders).
|
|
||||||
// Targets: FuelBoard (app) + FuelBoardWidgets (widget extension, embedded).
|
|
||||||
// Shared/ is a synchronized root group included in BOTH targets.
|
|
||||||
{
|
{
|
||||||
archiveVersion = 1;
|
archiveVersion = 1;
|
||||||
classes = {
|
classes = {
|
||||||
@@ -10,10 +7,26 @@
|
|||||||
objects = {
|
objects = {
|
||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
|
ADCE5B5A3033A32C008C131C /* FuelBoardWatch Watch App.app in Embed Watch Content */ = {isa = PBXBuildFile; fileRef = ADCE5B4F3033A329008C131C /* FuelBoardWatch Watch App.app */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||||
|
ADCE5B623033A32D008C131C /* FuelBoardWatch Watch App Extension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = ADCE5B633033A32D008C131C /* FuelBoardWatch Watch App Extension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||||
BB0000000000000000000001 /* FuelBoardWidgets.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = BB0000000000000000000002 /* FuelBoardWidgets.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
BB0000000000000000000001 /* FuelBoardWidgets.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = BB0000000000000000000002 /* FuelBoardWidgets.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
/* Begin PBXContainerItemProxy section */
|
/* Begin PBXContainerItemProxy section */
|
||||||
|
ADCE5B643033A32D008C131C /* PBXContainerItemProxy */ = {
|
||||||
|
isa = PBXContainerItemProxy;
|
||||||
|
containerPortal = BB0000000000000000000004 /* Project object */;
|
||||||
|
proxyType = 1;
|
||||||
|
remoteGlobalIDString = ADCE5B4E3033A329008C131C;
|
||||||
|
remoteInfo = "FuelBoardWatch Watch App Extension";
|
||||||
|
};
|
||||||
|
ADCE5B6F3033A32F008C131C /* PBXContainerItemProxy */ = {
|
||||||
|
isa = PBXContainerItemProxy;
|
||||||
|
containerPortal = BB0000000000000000000004 /* Project object */;
|
||||||
|
proxyType = 1;
|
||||||
|
remoteGlobalIDString = ADCE5B673033A32D008C131C;
|
||||||
|
remoteInfo = "FuelBoardWatch Watch App";
|
||||||
|
};
|
||||||
BB0000000000000000000003 /* PBXContainerItemProxy */ = {
|
BB0000000000000000000003 /* PBXContainerItemProxy */ = {
|
||||||
isa = PBXContainerItemProxy;
|
isa = PBXContainerItemProxy;
|
||||||
containerPortal = BB0000000000000000000004 /* Project object */;
|
containerPortal = BB0000000000000000000004 /* Project object */;
|
||||||
@@ -24,6 +37,28 @@
|
|||||||
/* End PBXContainerItemProxy section */
|
/* End PBXContainerItemProxy section */
|
||||||
|
|
||||||
/* Begin PBXCopyFilesBuildPhase section */
|
/* Begin PBXCopyFilesBuildPhase section */
|
||||||
|
ADCE5B653033A32D008C131C /* Embed App Extensions */ = {
|
||||||
|
isa = PBXCopyFilesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
dstPath = "";
|
||||||
|
dstSubfolderSpec = 13;
|
||||||
|
files = (
|
||||||
|
ADCE5B623033A32D008C131C /* FuelBoardWatch Watch App Extension.appex in Embed App Extensions */,
|
||||||
|
);
|
||||||
|
name = "Embed App Extensions";
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
ADCE5B5B3033A32C008C131C /* Embed Watch Content */ = {
|
||||||
|
isa = PBXCopyFilesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
dstPath = "$(CONTENTS_FOLDER_PATH)/Watch";
|
||||||
|
dstSubfolderSpec = 16;
|
||||||
|
files = (
|
||||||
|
ADCE5B5A3033A32C008C131C /* FuelBoardWatch Watch App.app in Embed Watch Content */,
|
||||||
|
);
|
||||||
|
name = "Embed Watch Content";
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
BB0000000000000000000006 /* Embed App Extensions */ = {
|
BB0000000000000000000006 /* Embed App Extensions */ = {
|
||||||
isa = PBXCopyFilesBuildPhase;
|
isa = PBXCopyFilesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
@@ -38,11 +73,23 @@
|
|||||||
/* End PBXCopyFilesBuildPhase section */
|
/* End PBXCopyFilesBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
|
ADCE5B4F3033A329008C131C /* FuelBoardWatch Watch App.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "FuelBoardWatch Watch App.app"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
|
ADCE5B633033A32D008C131C /* FuelBoardWatch Watch App Extension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = "FuelBoardWatch Watch App Extension.appex"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
BB0000000000000000000002 /* FuelBoardWidgets.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = FuelBoardWidgets.appex; sourceTree = BUILT_PRODUCTS_DIR; };
|
BB0000000000000000000002 /* FuelBoardWidgets.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = FuelBoardWidgets.appex; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
BB0000000000000000000007 /* FuelBoard.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = FuelBoard.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
BB0000000000000000000007 /* FuelBoard.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = FuelBoard.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
/* Begin PBXFileSystemSynchronizedRootGroup section */
|
/* Begin PBXFileSystemSynchronizedRootGroup section */
|
||||||
|
ADCE5B503033A329008C131C /* FuelBoardWatch Watch App */ = {
|
||||||
|
isa = PBXFileSystemSynchronizedRootGroup;
|
||||||
|
path = "FuelBoardWatch Watch App";
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
ADCE5B663033A32D008C131C /* FuelBoardWatch Wrapper */ = {
|
||||||
|
isa = PBXFileSystemSynchronizedRootGroup;
|
||||||
|
path = "FuelBoardWatch Wrapper";
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
BB0000000000000000000008 /* FuelBoard */ = {
|
BB0000000000000000000008 /* FuelBoard */ = {
|
||||||
isa = PBXFileSystemSynchronizedRootGroup;
|
isa = PBXFileSystemSynchronizedRootGroup;
|
||||||
path = FuelBoard;
|
path = FuelBoard;
|
||||||
@@ -61,6 +108,20 @@
|
|||||||
/* End PBXFileSystemSynchronizedRootGroup section */
|
/* End PBXFileSystemSynchronizedRootGroup section */
|
||||||
|
|
||||||
/* Begin PBXFrameworksBuildPhase section */
|
/* Begin PBXFrameworksBuildPhase section */
|
||||||
|
ADCE5B4C3033A329008C131C /* Frameworks */ = {
|
||||||
|
isa = PBXFrameworksBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
ADCE5B6D3033A32E008C131C /* Frameworks */ = {
|
||||||
|
isa = PBXFrameworksBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
BB000000000000000000000B /* Frameworks */ = {
|
BB000000000000000000000B /* Frameworks */ = {
|
||||||
isa = PBXFrameworksBuildPhase;
|
isa = PBXFrameworksBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
@@ -84,6 +145,8 @@
|
|||||||
BB0000000000000000000008 /* FuelBoard */,
|
BB0000000000000000000008 /* FuelBoard */,
|
||||||
BB0000000000000000000009 /* FuelBoardWidgets */,
|
BB0000000000000000000009 /* FuelBoardWidgets */,
|
||||||
BB000000000000000000000A /* Shared */,
|
BB000000000000000000000A /* Shared */,
|
||||||
|
ADCE5B663033A32D008C131C /* FuelBoardWatch Wrapper */,
|
||||||
|
ADCE5B503033A329008C131C /* FuelBoardWatch Watch App */,
|
||||||
BB000000000000000000000E /* Products */,
|
BB000000000000000000000E /* Products */,
|
||||||
);
|
);
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -93,6 +156,8 @@
|
|||||||
children = (
|
children = (
|
||||||
BB0000000000000000000007 /* FuelBoard.app */,
|
BB0000000000000000000007 /* FuelBoard.app */,
|
||||||
BB0000000000000000000002 /* FuelBoardWidgets.appex */,
|
BB0000000000000000000002 /* FuelBoardWidgets.appex */,
|
||||||
|
ADCE5B4F3033A329008C131C /* FuelBoardWatch Watch App.app */,
|
||||||
|
ADCE5B633033A32D008C131C /* FuelBoardWatch Watch App Extension.appex */,
|
||||||
);
|
);
|
||||||
name = Products;
|
name = Products;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -100,30 +165,50 @@
|
|||||||
/* End PBXGroup section */
|
/* End PBXGroup section */
|
||||||
|
|
||||||
/* Begin PBXNativeTarget section */
|
/* Begin PBXNativeTarget section */
|
||||||
BB000000000000000000000F /* FuelBoard */ = {
|
ADCE5B4E3033A329008C131C /* FuelBoardWatch Watch App */ = {
|
||||||
isa = PBXNativeTarget;
|
isa = PBXNativeTarget;
|
||||||
buildConfigurationList = BB0000000000000000000010 /* Build configuration list for PBXNativeTarget "FuelBoard" */;
|
buildConfigurationList = ADCE5B593033A32B008C131C /* Build configuration list for PBXNativeTarget "FuelBoardWatch Watch App" */;
|
||||||
buildPhases = (
|
buildPhases = (
|
||||||
BB0000000000000000000011 /* Sources */,
|
ADCE5B4B3033A329008C131C /* Sources */,
|
||||||
BB000000000000000000000B /* Frameworks */,
|
ADCE5B4C3033A329008C131C /* Frameworks */,
|
||||||
BB0000000000000000000012 /* Resources */,
|
ADCE5B4D3033A329008C131C /* Resources */,
|
||||||
BB0000000000000000000006 /* Embed App Extensions */,
|
|
||||||
);
|
);
|
||||||
buildRules = (
|
buildRules = (
|
||||||
);
|
);
|
||||||
dependencies = (
|
dependencies = (
|
||||||
BB0000000000000000000013 /* PBXTargetDependency */,
|
|
||||||
);
|
);
|
||||||
fileSystemSynchronizedGroups = (
|
fileSystemSynchronizedGroups = (
|
||||||
BB0000000000000000000008 /* FuelBoard */,
|
ADCE5B503033A329008C131C /* FuelBoardWatch Watch App */,
|
||||||
BB000000000000000000000A /* Shared */,
|
|
||||||
);
|
);
|
||||||
name = FuelBoard;
|
name = "FuelBoardWatch Watch App Extension";
|
||||||
packageProductDependencies = (
|
packageProductDependencies = (
|
||||||
);
|
);
|
||||||
productName = FuelBoard;
|
productName = "FuelBoardWatch Watch App Extension";
|
||||||
productReference = BB0000000000000000000007 /* FuelBoard.app */;
|
productReference = ADCE5B633033A32D008C131C /* FuelBoardWatch Watch App Extension.appex */;
|
||||||
productType = "com.apple.product-type.application";
|
productType = "com.apple.product-type.watchkit2-extension";
|
||||||
|
};
|
||||||
|
ADCE5B673033A32D008C131C /* FuelBoardWatch Watch App */ = {
|
||||||
|
isa = PBXNativeTarget;
|
||||||
|
buildConfigurationList = ADCE5B6A3033A32D008C131C /* Build configuration list for PBXNativeTarget "FuelBoardWatch Watch App" */;
|
||||||
|
buildPhases = (
|
||||||
|
ADCE5B6E3033A32E008C131C /* Resources */,
|
||||||
|
ADCE5B653033A32D008C131C /* Embed App Extensions */,
|
||||||
|
ADCE5B6D3033A32E008C131C /* Frameworks */,
|
||||||
|
);
|
||||||
|
buildRules = (
|
||||||
|
);
|
||||||
|
dependencies = (
|
||||||
|
ADCE5B683033A32D008C131C /* PBXTargetDependency */,
|
||||||
|
);
|
||||||
|
fileSystemSynchronizedGroups = (
|
||||||
|
ADCE5B663033A32D008C131C /* FuelBoardWatch Wrapper */,
|
||||||
|
);
|
||||||
|
name = "FuelBoardWatch Watch App";
|
||||||
|
packageProductDependencies = (
|
||||||
|
);
|
||||||
|
productName = "FuelBoardWatch Watch App";
|
||||||
|
productReference = ADCE5B4F3033A329008C131C /* FuelBoardWatch Watch App.app */;
|
||||||
|
productType = "com.apple.product-type.application.watchapp2";
|
||||||
};
|
};
|
||||||
BB0000000000000000000005 /* FuelBoardWidgets */ = {
|
BB0000000000000000000005 /* FuelBoardWidgets */ = {
|
||||||
isa = PBXNativeTarget;
|
isa = PBXNativeTarget;
|
||||||
@@ -148,6 +233,33 @@
|
|||||||
productReference = BB0000000000000000000002 /* FuelBoardWidgets.appex */;
|
productReference = BB0000000000000000000002 /* FuelBoardWidgets.appex */;
|
||||||
productType = "com.apple.product-type.app-extension";
|
productType = "com.apple.product-type.app-extension";
|
||||||
};
|
};
|
||||||
|
BB000000000000000000000F /* FuelBoard */ = {
|
||||||
|
isa = PBXNativeTarget;
|
||||||
|
buildConfigurationList = BB0000000000000000000010 /* Build configuration list for PBXNativeTarget "FuelBoard" */;
|
||||||
|
buildPhases = (
|
||||||
|
BB0000000000000000000011 /* Sources */,
|
||||||
|
BB000000000000000000000B /* Frameworks */,
|
||||||
|
BB0000000000000000000012 /* Resources */,
|
||||||
|
ADCE5B5B3033A32C008C131C /* Embed Watch Content */,
|
||||||
|
BB0000000000000000000006 /* Embed App Extensions */,
|
||||||
|
);
|
||||||
|
buildRules = (
|
||||||
|
);
|
||||||
|
dependencies = (
|
||||||
|
ADCE5B703033A32F008C131C /* PBXTargetDependency */,
|
||||||
|
BB0000000000000000000013 /* PBXTargetDependency */,
|
||||||
|
);
|
||||||
|
fileSystemSynchronizedGroups = (
|
||||||
|
BB0000000000000000000008 /* FuelBoard */,
|
||||||
|
BB000000000000000000000A /* Shared */,
|
||||||
|
);
|
||||||
|
name = FuelBoard;
|
||||||
|
packageProductDependencies = (
|
||||||
|
);
|
||||||
|
productName = FuelBoard;
|
||||||
|
productReference = BB0000000000000000000007 /* FuelBoard.app */;
|
||||||
|
productType = "com.apple.product-type.application";
|
||||||
|
};
|
||||||
/* End PBXNativeTarget section */
|
/* End PBXNativeTarget section */
|
||||||
|
|
||||||
/* Begin PBXProject section */
|
/* Begin PBXProject section */
|
||||||
@@ -155,15 +267,18 @@
|
|||||||
isa = PBXProject;
|
isa = PBXProject;
|
||||||
attributes = {
|
attributes = {
|
||||||
BuildIndependentTargetsInParallel = 1;
|
BuildIndependentTargetsInParallel = 1;
|
||||||
LastSwiftUpdateCheck = 1600;
|
LastSwiftUpdateCheck = 2630;
|
||||||
LastUpgradeCheck = 1600;
|
LastUpgradeCheck = 1600;
|
||||||
TargetAttributes = {
|
TargetAttributes = {
|
||||||
BB000000000000000000000F = {
|
ADCE5B4E3033A329008C131C = {
|
||||||
CreatedOnToolsVersion = 16.0;
|
CreatedOnToolsVersion = 26.3;
|
||||||
};
|
};
|
||||||
BB0000000000000000000005 = {
|
BB0000000000000000000005 = {
|
||||||
CreatedOnToolsVersion = 16.0;
|
CreatedOnToolsVersion = 16.0;
|
||||||
};
|
};
|
||||||
|
BB000000000000000000000F = {
|
||||||
|
CreatedOnToolsVersion = 16.0;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
buildConfigurationList = BB0000000000000000000017 /* Build configuration list for PBXProject "FuelBoard" */;
|
buildConfigurationList = BB0000000000000000000017 /* Build configuration list for PBXProject "FuelBoard" */;
|
||||||
@@ -182,11 +297,27 @@
|
|||||||
targets = (
|
targets = (
|
||||||
BB000000000000000000000F /* FuelBoard */,
|
BB000000000000000000000F /* FuelBoard */,
|
||||||
BB0000000000000000000005 /* FuelBoardWidgets */,
|
BB0000000000000000000005 /* FuelBoardWidgets */,
|
||||||
|
ADCE5B673033A32D008C131C /* FuelBoardWatch Watch App */,
|
||||||
|
ADCE5B4E3033A329008C131C /* FuelBoardWatch Watch App Extension */,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
/* End PBXProject section */
|
/* End PBXProject section */
|
||||||
|
|
||||||
/* Begin PBXResourcesBuildPhase section */
|
/* Begin PBXResourcesBuildPhase section */
|
||||||
|
ADCE5B4D3033A329008C131C /* Resources */ = {
|
||||||
|
isa = PBXResourcesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
ADCE5B6E3033A32E008C131C /* Resources */ = {
|
||||||
|
isa = PBXResourcesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
BB0000000000000000000012 /* Resources */ = {
|
BB0000000000000000000012 /* Resources */ = {
|
||||||
isa = PBXResourcesBuildPhase;
|
isa = PBXResourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
@@ -204,6 +335,13 @@
|
|||||||
/* End PBXResourcesBuildPhase section */
|
/* End PBXResourcesBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXSourcesBuildPhase section */
|
/* Begin PBXSourcesBuildPhase section */
|
||||||
|
ADCE5B4B3033A329008C131C /* Sources */ = {
|
||||||
|
isa = PBXSourcesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
BB0000000000000000000011 /* Sources */ = {
|
BB0000000000000000000011 /* Sources */ = {
|
||||||
isa = PBXSourcesBuildPhase;
|
isa = PBXSourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
@@ -221,6 +359,16 @@
|
|||||||
/* End PBXSourcesBuildPhase section */
|
/* End PBXSourcesBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXTargetDependency section */
|
/* Begin PBXTargetDependency section */
|
||||||
|
ADCE5B683033A32D008C131C /* PBXTargetDependency */ = {
|
||||||
|
isa = PBXTargetDependency;
|
||||||
|
target = ADCE5B4E3033A329008C131C /* FuelBoardWatch Watch App Extension */;
|
||||||
|
targetProxy = ADCE5B643033A32D008C131C /* PBXContainerItemProxy */;
|
||||||
|
};
|
||||||
|
ADCE5B703033A32F008C131C /* PBXTargetDependency */ = {
|
||||||
|
isa = PBXTargetDependency;
|
||||||
|
target = ADCE5B673033A32D008C131C /* FuelBoardWatch Watch App */;
|
||||||
|
targetProxy = ADCE5B6F3033A32F008C131C /* PBXContainerItemProxy */;
|
||||||
|
};
|
||||||
BB0000000000000000000013 /* PBXTargetDependency */ = {
|
BB0000000000000000000013 /* PBXTargetDependency */ = {
|
||||||
isa = PBXTargetDependency;
|
isa = PBXTargetDependency;
|
||||||
target = BB0000000000000000000005 /* FuelBoardWidgets */;
|
target = BB0000000000000000000005 /* FuelBoardWidgets */;
|
||||||
@@ -229,6 +377,266 @@
|
|||||||
/* End PBXTargetDependency section */
|
/* End PBXTargetDependency section */
|
||||||
|
|
||||||
/* Begin XCBuildConfiguration section */
|
/* Begin XCBuildConfiguration section */
|
||||||
|
ADCE5B573033A32B008C131C /* Debug */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
|
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||||
|
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||||
|
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||||
|
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
||||||
|
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||||
|
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||||
|
CLANG_WARN_COMMA = YES;
|
||||||
|
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||||
|
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||||
|
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||||
|
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||||
|
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||||
|
CLANG_WARN_INT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||||
|
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||||
|
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||||
|
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||||
|
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||||
|
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||||
|
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||||
|
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||||
|
CODE_SIGN_STYLE = Automatic;
|
||||||
|
CURRENT_PROJECT_VERSION = 3;
|
||||||
|
DEVELOPMENT_TEAM = F3BE6NE7U3;
|
||||||
|
ENABLE_PREVIEWS = YES;
|
||||||
|
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||||
|
GCC_C_LANGUAGE_STANDARD = gnu17;
|
||||||
|
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||||
|
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||||
|
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||||
|
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||||
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||||
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
|
GENERATE_INFOPLIST_FILE = NO;
|
||||||
|
CODE_SIGN_ENTITLEMENTS = Config/WatchApp.entitlements;
|
||||||
|
INFOPLIST_FILE = Config/WatchExtension-Info.plist;
|
||||||
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"@executable_path/Frameworks",
|
||||||
|
);
|
||||||
|
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
||||||
|
MARKETING_VERSION = 1.0.1;
|
||||||
|
MTL_FAST_MATH = YES;
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = com.apt.fuelboard.watchapp.watchkitextension;
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
SDKROOT = watchos;
|
||||||
|
SKIP_INSTALL = YES;
|
||||||
|
STRING_CATALOG_GENERATE_SYMBOLS = YES;
|
||||||
|
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)";
|
||||||
|
SWIFT_APPROACHABLE_CONCURRENCY = YES;
|
||||||
|
SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor;
|
||||||
|
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||||
|
SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES;
|
||||||
|
SWIFT_VERSION = 5.0;
|
||||||
|
TARGETED_DEVICE_FAMILY = 4;
|
||||||
|
WATCHOS_DEPLOYMENT_TARGET = 26.2;
|
||||||
|
};
|
||||||
|
name = Debug;
|
||||||
|
};
|
||||||
|
ADCE5B583033A32B008C131C /* Release */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
|
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||||
|
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||||
|
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||||
|
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
||||||
|
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||||
|
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||||
|
CLANG_WARN_COMMA = YES;
|
||||||
|
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||||
|
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||||
|
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||||
|
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||||
|
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||||
|
CLANG_WARN_INT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||||
|
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||||
|
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||||
|
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||||
|
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||||
|
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||||
|
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||||
|
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||||
|
CODE_SIGN_STYLE = Automatic;
|
||||||
|
CURRENT_PROJECT_VERSION = 3;
|
||||||
|
DEVELOPMENT_TEAM = F3BE6NE7U3;
|
||||||
|
ENABLE_PREVIEWS = YES;
|
||||||
|
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||||
|
GCC_C_LANGUAGE_STANDARD = gnu17;
|
||||||
|
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||||
|
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||||
|
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||||
|
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||||
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||||
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
|
GENERATE_INFOPLIST_FILE = NO;
|
||||||
|
CODE_SIGN_ENTITLEMENTS = Config/WatchApp.entitlements;
|
||||||
|
INFOPLIST_FILE = Config/WatchExtension-Info.plist;
|
||||||
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"@executable_path/Frameworks",
|
||||||
|
);
|
||||||
|
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
||||||
|
MARKETING_VERSION = 1.0.1;
|
||||||
|
MTL_FAST_MATH = YES;
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = com.apt.fuelboard.watchapp.watchkitextension;
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
SDKROOT = watchos;
|
||||||
|
SKIP_INSTALL = YES;
|
||||||
|
STRING_CATALOG_GENERATE_SYMBOLS = YES;
|
||||||
|
SWIFT_APPROACHABLE_CONCURRENCY = YES;
|
||||||
|
SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor;
|
||||||
|
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||||
|
SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES;
|
||||||
|
SWIFT_VERSION = 5.0;
|
||||||
|
TARGETED_DEVICE_FAMILY = 4;
|
||||||
|
WATCHOS_DEPLOYMENT_TARGET = 26.2;
|
||||||
|
};
|
||||||
|
name = Release;
|
||||||
|
};
|
||||||
|
ADCE5B6B3033A32D008C131C /* Debug */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
|
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||||
|
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||||
|
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||||
|
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
||||||
|
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||||
|
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||||
|
CLANG_WARN_COMMA = YES;
|
||||||
|
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||||
|
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||||
|
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||||
|
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||||
|
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||||
|
CLANG_WARN_INT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||||
|
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||||
|
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||||
|
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||||
|
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||||
|
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||||
|
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||||
|
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||||
|
CODE_SIGN_STYLE = Automatic;
|
||||||
|
CURRENT_PROJECT_VERSION = 3;
|
||||||
|
DEVELOPMENT_TEAM = F3BE6NE7U3;
|
||||||
|
ENABLE_PREVIEWS = YES;
|
||||||
|
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||||
|
GCC_C_LANGUAGE_STANDARD = gnu17;
|
||||||
|
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||||
|
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||||
|
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||||
|
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||||
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||||
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
|
GENERATE_INFOPLIST_FILE = NO;
|
||||||
|
INFOPLIST_FILE = Config/WatchWrapper-Info.plist;
|
||||||
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"@executable_path/Frameworks",
|
||||||
|
);
|
||||||
|
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
||||||
|
MARKETING_VERSION = 1.0.1;
|
||||||
|
MTL_FAST_MATH = YES;
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = com.apt.fuelboard.watchapp;
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
SDKROOT = watchos;
|
||||||
|
SKIP_INSTALL = YES;
|
||||||
|
STRING_CATALOG_GENERATE_SYMBOLS = YES;
|
||||||
|
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)";
|
||||||
|
SWIFT_APPROACHABLE_CONCURRENCY = YES;
|
||||||
|
SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor;
|
||||||
|
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||||
|
SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES;
|
||||||
|
SWIFT_VERSION = 5.0;
|
||||||
|
TARGETED_DEVICE_FAMILY = 4;
|
||||||
|
WATCHOS_DEPLOYMENT_TARGET = 26.2;
|
||||||
|
};
|
||||||
|
name = Debug;
|
||||||
|
};
|
||||||
|
ADCE5B6C3033A32D008C131C /* Release */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
|
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||||
|
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||||
|
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||||
|
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
||||||
|
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||||
|
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||||
|
CLANG_WARN_COMMA = YES;
|
||||||
|
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||||
|
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||||
|
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||||
|
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||||
|
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||||
|
CLANG_WARN_INT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||||
|
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||||
|
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||||
|
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||||
|
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||||
|
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||||
|
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||||
|
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||||
|
CODE_SIGN_STYLE = Automatic;
|
||||||
|
CURRENT_PROJECT_VERSION = 3;
|
||||||
|
DEVELOPMENT_TEAM = F3BE6NE7U3;
|
||||||
|
ENABLE_PREVIEWS = YES;
|
||||||
|
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||||
|
GCC_C_LANGUAGE_STANDARD = gnu17;
|
||||||
|
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||||
|
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||||
|
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||||
|
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||||
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||||
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
|
GENERATE_INFOPLIST_FILE = NO;
|
||||||
|
INFOPLIST_FILE = Config/WatchWrapper-Info.plist;
|
||||||
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"@executable_path/Frameworks",
|
||||||
|
);
|
||||||
|
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
||||||
|
MARKETING_VERSION = 1.0.1;
|
||||||
|
MTL_FAST_MATH = YES;
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = com.apt.fuelboard.watchapp;
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
SDKROOT = watchos;
|
||||||
|
SKIP_INSTALL = YES;
|
||||||
|
STRING_CATALOG_GENERATE_SYMBOLS = YES;
|
||||||
|
SWIFT_APPROACHABLE_CONCURRENCY = YES;
|
||||||
|
SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor;
|
||||||
|
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||||
|
SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES;
|
||||||
|
SWIFT_VERSION = 5.0;
|
||||||
|
TARGETED_DEVICE_FAMILY = 4;
|
||||||
|
WATCHOS_DEPLOYMENT_TARGET = 26.2;
|
||||||
|
};
|
||||||
|
name = Release;
|
||||||
|
};
|
||||||
BB0000000000000000000018 /* Debug */ = {
|
BB0000000000000000000018 /* Debug */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
@@ -251,7 +659,7 @@
|
|||||||
"DEBUG=1",
|
"DEBUG=1",
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
);
|
);
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 17.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 26.0;
|
||||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||||
ONLY_ACTIVE_ARCH = YES;
|
ONLY_ACTIVE_ARCH = YES;
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
@@ -276,7 +684,7 @@
|
|||||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||||
GCC_NO_COMMON_BLOCKS = YES;
|
GCC_NO_COMMON_BLOCKS = YES;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 17.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 26.0;
|
||||||
MTL_ENABLE_DEBUG_INFO = NO;
|
MTL_ENABLE_DEBUG_INFO = NO;
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
SWIFT_COMPILATION_MODE = wholemodule;
|
SWIFT_COMPILATION_MODE = wholemodule;
|
||||||
@@ -288,16 +696,18 @@
|
|||||||
BB000000000000000000001A /* Debug */ = {
|
BB000000000000000000001A /* Debug */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
CODE_SIGN_ENTITLEMENTS = Config/App.entitlements;
|
CODE_SIGN_ENTITLEMENTS = Config/App.entitlements;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 3;
|
||||||
|
DEVELOPMENT_TEAM = F3BE6NE7U3;
|
||||||
GENERATE_INFOPLIST_FILE = NO;
|
GENERATE_INFOPLIST_FILE = NO;
|
||||||
INFOPLIST_FILE = Config/App-Info.plist;
|
INFOPLIST_FILE = "Config/App-Info.plist";
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 1.0;
|
MARKETING_VERSION = 1.0.1;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.apt.fuelboard;
|
PRODUCT_BUNDLE_IDENTIFIER = com.apt.fuelboard;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||||
@@ -309,16 +719,18 @@
|
|||||||
BB000000000000000000001B /* Release */ = {
|
BB000000000000000000001B /* Release */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
CODE_SIGN_ENTITLEMENTS = Config/App.entitlements;
|
CODE_SIGN_ENTITLEMENTS = Config/App.entitlements;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 3;
|
||||||
|
DEVELOPMENT_TEAM = F3BE6NE7U3;
|
||||||
GENERATE_INFOPLIST_FILE = NO;
|
GENERATE_INFOPLIST_FILE = NO;
|
||||||
INFOPLIST_FILE = Config/App-Info.plist;
|
INFOPLIST_FILE = "Config/App-Info.plist";
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 1.0;
|
MARKETING_VERSION = 1.0.1;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.apt.fuelboard;
|
PRODUCT_BUNDLE_IDENTIFIER = com.apt.fuelboard;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||||
@@ -333,15 +745,16 @@
|
|||||||
APPLICATION_EXTENSION_API_ONLY = YES;
|
APPLICATION_EXTENSION_API_ONLY = YES;
|
||||||
CODE_SIGN_ENTITLEMENTS = Config/Widget.entitlements;
|
CODE_SIGN_ENTITLEMENTS = Config/Widget.entitlements;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 3;
|
||||||
|
DEVELOPMENT_TEAM = F3BE6NE7U3;
|
||||||
GENERATE_INFOPLIST_FILE = NO;
|
GENERATE_INFOPLIST_FILE = NO;
|
||||||
INFOPLIST_FILE = Config/Widget-Info.plist;
|
INFOPLIST_FILE = "Config/Widget-Info.plist";
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
"@executable_path/../../Frameworks",
|
"@executable_path/../../Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 1.0;
|
MARKETING_VERSION = 1.0.1;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.apt.fuelboard.widgets;
|
PRODUCT_BUNDLE_IDENTIFIER = com.apt.fuelboard.widgets;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SKIP_INSTALL = YES;
|
SKIP_INSTALL = YES;
|
||||||
@@ -357,15 +770,16 @@
|
|||||||
APPLICATION_EXTENSION_API_ONLY = YES;
|
APPLICATION_EXTENSION_API_ONLY = YES;
|
||||||
CODE_SIGN_ENTITLEMENTS = Config/Widget.entitlements;
|
CODE_SIGN_ENTITLEMENTS = Config/Widget.entitlements;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 3;
|
||||||
|
DEVELOPMENT_TEAM = F3BE6NE7U3;
|
||||||
GENERATE_INFOPLIST_FILE = NO;
|
GENERATE_INFOPLIST_FILE = NO;
|
||||||
INFOPLIST_FILE = Config/Widget-Info.plist;
|
INFOPLIST_FILE = "Config/Widget-Info.plist";
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
"@executable_path/../../Frameworks",
|
"@executable_path/../../Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 1.0;
|
MARKETING_VERSION = 1.0.1;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.apt.fuelboard.widgets;
|
PRODUCT_BUNDLE_IDENTIFIER = com.apt.fuelboard.widgets;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SKIP_INSTALL = YES;
|
SKIP_INSTALL = YES;
|
||||||
@@ -378,11 +792,20 @@
|
|||||||
/* End XCBuildConfiguration section */
|
/* End XCBuildConfiguration section */
|
||||||
|
|
||||||
/* Begin XCConfigurationList section */
|
/* Begin XCConfigurationList section */
|
||||||
BB0000000000000000000017 /* Build configuration list for PBXProject "FuelBoard" */ = {
|
ADCE5B593033A32B008C131C /* Build configuration list for PBXNativeTarget "FuelBoardWatch Watch App" */ = {
|
||||||
isa = XCConfigurationList;
|
isa = XCConfigurationList;
|
||||||
buildConfigurations = (
|
buildConfigurations = (
|
||||||
BB0000000000000000000018 /* Debug */,
|
ADCE5B573033A32B008C131C /* Debug */,
|
||||||
BB0000000000000000000019 /* Release */,
|
ADCE5B583033A32B008C131C /* Release */,
|
||||||
|
);
|
||||||
|
defaultConfigurationIsVisible = 0;
|
||||||
|
defaultConfigurationName = Release;
|
||||||
|
};
|
||||||
|
ADCE5B6A3033A32D008C131C /* Build configuration list for PBXNativeTarget "FuelBoardWatch Watch App" */ = {
|
||||||
|
isa = XCConfigurationList;
|
||||||
|
buildConfigurations = (
|
||||||
|
ADCE5B6B3033A32D008C131C /* Debug */,
|
||||||
|
ADCE5B6C3033A32D008C131C /* Release */,
|
||||||
);
|
);
|
||||||
defaultConfigurationIsVisible = 0;
|
defaultConfigurationIsVisible = 0;
|
||||||
defaultConfigurationName = Release;
|
defaultConfigurationName = Release;
|
||||||
@@ -405,6 +828,15 @@
|
|||||||
defaultConfigurationIsVisible = 0;
|
defaultConfigurationIsVisible = 0;
|
||||||
defaultConfigurationName = Release;
|
defaultConfigurationName = Release;
|
||||||
};
|
};
|
||||||
|
BB0000000000000000000017 /* Build configuration list for PBXProject "FuelBoard" */ = {
|
||||||
|
isa = XCConfigurationList;
|
||||||
|
buildConfigurations = (
|
||||||
|
BB0000000000000000000018 /* Debug */,
|
||||||
|
BB0000000000000000000019 /* Release */,
|
||||||
|
);
|
||||||
|
defaultConfigurationIsVisible = 0;
|
||||||
|
defaultConfigurationName = Release;
|
||||||
|
};
|
||||||
/* End XCConfigurationList section */
|
/* End XCConfigurationList section */
|
||||||
};
|
};
|
||||||
rootObject = BB0000000000000000000004 /* Project object */;
|
rootObject = BB0000000000000000000004 /* Project object */;
|
||||||
|
|||||||
@@ -1,58 +1,204 @@
|
|||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
/// Alerts tab — enables the cheapest-station proximity alerts and shows what's
|
/// Alerts tab — enables the cheapest-station proximity alerts. The fuel being
|
||||||
/// being monitored.
|
/// monitored is chosen HERE, independently of the Stations-tab selection:
|
||||||
|
/// users can browse any fuel without re-targeting their alerts (and vice
|
||||||
|
/// versa). The Live Activity section also lives here — it mirrors this page's
|
||||||
|
/// shape (toggle + fuel + distance) so the two "notify me while driving"
|
||||||
|
/// surfaces sit together. Geofence plumbing (count, 18-limit) lives in
|
||||||
|
/// Settings → Debug; the user-facing page keeps a plain-language explainer
|
||||||
|
/// plus a 0-stations hint.
|
||||||
struct AlertsView: View {
|
struct AlertsView: View {
|
||||||
@Binding var enabled: Bool
|
@Binding var enabled: Bool
|
||||||
@Binding var radius: Double
|
@Binding var radius: Double // stored in km (monitor + storage)
|
||||||
|
@Binding var fuel: FuelType // the fuel alerts monitor for
|
||||||
|
@Binding var favouriteDropEnabled: Bool
|
||||||
|
@Binding var favouriteDropFuel: FuelType
|
||||||
|
/// Whether alerts mirror the Stations-tab search distance (capped at 8 mi).
|
||||||
|
@Binding var followsSearch: Bool
|
||||||
|
/// Whether the "cheapest nearby" Live Activity is shown on the Lock Screen
|
||||||
|
/// / Dynamic Island. Tracks its OWN fuel + radius (below) — independent of
|
||||||
|
/// the Stations tab.
|
||||||
|
@Binding var liveActivityEnabled: Bool
|
||||||
|
/// Fuel the Live Activity tracks (independent of the Stations tab).
|
||||||
|
@Binding var liveActivityFuel: FuelType
|
||||||
|
/// Search radius (miles, 5/10/15) the Live Activity uses.
|
||||||
|
@Binding var liveActivityRadiusMiles: Int
|
||||||
|
/// Whether the Live Activity mirrors the Stations-tab search distance.
|
||||||
|
@Binding var liveActivityFollowsSearch: Bool
|
||||||
|
/// Current Stations-tab search distance (miles) — the value "Follow search"
|
||||||
|
/// mirrors.
|
||||||
|
let stationLimit: Int
|
||||||
|
let distanceUnit: DistanceUnit
|
||||||
let monitoredCount: Int
|
let monitoredCount: Int
|
||||||
let lastAlert: String?
|
let lastAlert: String?
|
||||||
|
let onCheckFavouriteDropNow: () -> Void
|
||||||
|
|
||||||
|
/// The Stations-tab search distance shown in the user's unit, e.g.
|
||||||
|
/// "5 miles" or "8 km" — for the "Follow search" rows.
|
||||||
|
private var searchInUnitLabel: String {
|
||||||
|
let shown = distanceUnit.displayMiles(stationLimit) // stationLimit is always miles
|
||||||
|
return "\(shown) \(distanceUnit.label(for: Double(shown)))"
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A miles value (5/10/15) shown truthfully in the user's unit, e.g.
|
||||||
|
/// "5 miles" (miles unit) or "8 km" (km unit) — picker labels must match
|
||||||
|
/// what the option really means.
|
||||||
|
private func distanceLabel(forMiles miles: Int) -> String {
|
||||||
|
let shown = distanceUnit.displayMiles(miles)
|
||||||
|
return "\(shown) \(distanceUnit.label(for: Double(shown)))"
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Situational guidance for the currently-selected alert radius, shown
|
||||||
|
/// under the picker so users know what each choice is best for. Labels
|
||||||
|
/// mirror the picker row text (unit-aware) and explain the trade-off:
|
||||||
|
/// smaller radii ping more often but later; larger radii ping rarely in
|
||||||
|
/// town but earlier from afar.
|
||||||
|
private var alertRadiusGuidance: String {
|
||||||
|
switch alertChoice.wrappedValue {
|
||||||
|
case .followSearch:
|
||||||
|
return "Set-and-forget: alerts behave exactly like the Stations-tab search distance (capped at 8 miles for reliable geofencing)."
|
||||||
|
case .fixed(let value):
|
||||||
|
let optionLabel = "\(value) \(distanceUnit.label(for: Double(value)))"
|
||||||
|
let body: String
|
||||||
|
switch value {
|
||||||
|
case 1:
|
||||||
|
body = "Town driving. Fires most often — you're rarely inside the circle for long, so pings are meaningful. Best for stop-start local trips."
|
||||||
|
case 2:
|
||||||
|
body = "City or suburb commute. Slightly earlier heads-up than the smallest option; still fires regularly around town."
|
||||||
|
case 3:
|
||||||
|
body = "Mixed driving. Middle ground: heads-up a few minutes out, still works in most towns."
|
||||||
|
case 5:
|
||||||
|
body = "A-roads and motorway exits. Wide early warning for longer trips — but in town it rarely fires, because you're usually already inside the circle."
|
||||||
|
default:
|
||||||
|
body = "Long-distance journeys. Maximum reach: a ping when a cheap station is coming up from far out. In town this essentially never fires."
|
||||||
|
}
|
||||||
|
return "\(optionLabel) — \(body)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum AlertRadiusChoice: Hashable {
|
||||||
|
case followSearch
|
||||||
|
case fixed(Int)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The radius picker works in the user's chosen unit; the stored value
|
||||||
|
/// stays km so ProximityMonitor and persistence never change. Options are
|
||||||
|
/// snapped to the nearest picker value so any stored radius still selects.
|
||||||
|
/// "Follow search" mirrors the Stations-tab distance (capped at 8 miles).
|
||||||
|
private var alertChoice: Binding<AlertRadiusChoice> {
|
||||||
|
Binding(
|
||||||
|
get: {
|
||||||
|
if followsSearch { return .followSearch }
|
||||||
|
let inUnit = distanceUnit.fromKM(radius)
|
||||||
|
let snapped = FuelStore.alertRadiusOptions
|
||||||
|
.min(by: { abs(Double($0) - inUnit) < abs(Double($1) - inUnit) }) ?? 3
|
||||||
|
return .fixed(snapped)
|
||||||
|
},
|
||||||
|
set: { choice in
|
||||||
|
switch choice {
|
||||||
|
case .followSearch:
|
||||||
|
followsSearch = true
|
||||||
|
case .fixed(let value):
|
||||||
|
followsSearch = false
|
||||||
|
radius = distanceUnit.toKM(Double(value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum LADistanceChoice: Hashable {
|
||||||
|
case followSearch
|
||||||
|
case fixed(Int)
|
||||||
|
}
|
||||||
|
|
||||||
|
private var laChoice: Binding<LADistanceChoice> {
|
||||||
|
Binding(
|
||||||
|
get: {
|
||||||
|
if liveActivityFollowsSearch { return .followSearch }
|
||||||
|
return .fixed(liveActivityRadiusMiles)
|
||||||
|
},
|
||||||
|
set: { choice in
|
||||||
|
switch choice {
|
||||||
|
case .followSearch:
|
||||||
|
liveActivityFollowsSearch = true
|
||||||
|
case .fixed(let miles):
|
||||||
|
liveActivityFollowsSearch = false
|
||||||
|
liveActivityRadiusMiles = miles
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
NavigationStack {
|
NavigationStack {
|
||||||
List {
|
List {
|
||||||
Section {
|
Section {
|
||||||
Toggle("Cheapest-station alerts", isOn: $enabled)
|
Toggle("Cheapest favourite", isOn: $favouriteDropEnabled)
|
||||||
|
Picker("Fuel", selection: $favouriteDropFuel) {
|
||||||
|
ForEach(FuelType.allCases) { fuel in
|
||||||
|
Text(fuel.displayName).tag(fuel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Button("Check now", action: onCheckFavouriteDropNow)
|
||||||
|
.disabled(!favouriteDropEnabled)
|
||||||
|
} header: {
|
||||||
|
Text("Price-drop alerts")
|
||||||
} footer: {
|
} footer: {
|
||||||
Text("When you approach a station that is the cheapest within the radius, FuelBoard sends a notification — even with the app closed.")
|
Text("Watches your cheapest saved favourite for the fuel you pick here. FuelBoard alerts when a different favourite becomes the cheapest, or when your current cheapest favourite drops by at least 1.0p. This is separate from the nearby-geofence alert, so you can monitor favourites without extra location noise.")
|
||||||
}
|
}
|
||||||
|
|
||||||
if enabled {
|
Section {
|
||||||
Section("Trigger radius") {
|
Toggle("Cheapest station", isOn: $enabled)
|
||||||
VStack(alignment: .leading, spacing: 8) {
|
Picker("Fuel", selection: $fuel) {
|
||||||
HStack {
|
ForEach(FuelType.allCases) { fuel in
|
||||||
Text("Radius")
|
Text(fuel.displayName).tag(fuel)
|
||||||
Spacer()
|
}
|
||||||
Text("\(Int(radius)) km")
|
}
|
||||||
|
Picker("Radius", selection: alertChoice) {
|
||||||
|
Text("Follow search (\(searchInUnitLabel))").tag(AlertRadiusChoice.followSearch)
|
||||||
|
ForEach(FuelStore.alertRadiusOptions, id: \.self) { value in
|
||||||
|
Text("\(value) \(distanceUnit.label(for: Double(value)))").tag(AlertRadiusChoice.fixed(value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Text(alertRadiusGuidance)
|
||||||
|
.font(.caption)
|
||||||
.foregroundStyle(.secondary)
|
.foregroundStyle(.secondary)
|
||||||
.monospacedDigit()
|
if enabled && monitoredCount == 0 {
|
||||||
}
|
|
||||||
Slider(value: $radius, in: 1...10, step: 1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Section("Monitoring") {
|
|
||||||
if monitoredCount == 0 {
|
|
||||||
Text("No stations monitored yet — open the Stations tab to load prices first.")
|
Text("No stations monitored yet — open the Stations tab to load prices first.")
|
||||||
.font(.caption)
|
.font(.caption)
|
||||||
.foregroundStyle(.secondary)
|
.foregroundStyle(.secondary)
|
||||||
} else {
|
|
||||||
LabeledContent("Geofenced stations", value: "\(monitoredCount)")
|
|
||||||
Text("Your favourites get priority, then the closest stations fill the rest (18 max, iOS region limit).")
|
|
||||||
.font(.caption)
|
|
||||||
.foregroundStyle(.secondary)
|
|
||||||
Text("Alerts are checked against the cheapest station within \(Int(radius)) km for the selected fuel. Each station alerts at most once per hour.")
|
|
||||||
.font(.caption)
|
|
||||||
.foregroundStyle(.secondary)
|
|
||||||
}
|
}
|
||||||
|
} header: {
|
||||||
|
Text("Proximity alerts")
|
||||||
|
} footer: {
|
||||||
|
Text("When you approach a station that is the cheapest within the radius, FuelBoard sends a notification — even with the app closed. Nearby stations selling \(fuel.displayName.lowercased()) are watched — favourites get priority — and each station alerts at most once per hour. Follow search mirrors the Stations-tab distance, capped at 8 miles for reliable geofencing. Smaller radii ping more often but later; larger radii ping rarely in town but earlier from afar.")
|
||||||
}
|
}
|
||||||
|
|
||||||
if let lastAlert {
|
if enabled, let lastAlert {
|
||||||
Section("Last alert") {
|
Section("Last alert") {
|
||||||
Label(lastAlert, systemImage: "bell.badge.fill")
|
Label(lastAlert, systemImage: "bell.badge.fill")
|
||||||
.font(.footnote)
|
.font(.footnote)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Section {
|
||||||
|
Toggle("Cheapest station", isOn: $liveActivityEnabled)
|
||||||
|
Picker("Fuel", selection: $liveActivityFuel) {
|
||||||
|
ForEach(FuelType.allCases) { fuel in
|
||||||
|
Text(fuel.displayName).tag(fuel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Picker("Distance", selection: laChoice) {
|
||||||
|
Text("Follow search (\(searchInUnitLabel))").tag(LADistanceChoice.followSearch)
|
||||||
|
ForEach(FuelStore.stationRadiusOptions, id: \.self) { miles in
|
||||||
|
Text(distanceLabel(forMiles: miles)).tag(LADistanceChoice.fixed(miles))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} header: {
|
||||||
|
Text("Live Activity")
|
||||||
|
} footer: {
|
||||||
|
Text("Shows the cheapest station for the fuel and distance you pick here on the Lock Screen and Dynamic Island — independent of the Stations tab (choose Follow search to mirror its distance instead). Updates as you drive; tap to open directions. Live Activities can be disabled in System Settings → Live Activities.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.navigationTitle("Alerts")
|
.navigationTitle("Alerts")
|
||||||
|
|||||||
|
After Width: | Height: | Size: 208 KiB |
|
After Width: | Height: | Size: 47 KiB |
|
After Width: | Height: | Size: 747 KiB |
@@ -0,0 +1,170 @@
|
|||||||
|
{
|
||||||
|
"images": [
|
||||||
|
{
|
||||||
|
"filename": "AppIcon.png",
|
||||||
|
"idiom": "universal",
|
||||||
|
"platform": "ios",
|
||||||
|
"size": "1024x1024"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"appearances": [
|
||||||
|
{
|
||||||
|
"appearance": "luminosity",
|
||||||
|
"value": "dark"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"filename": "AppIcon-dark.png",
|
||||||
|
"idiom": "universal",
|
||||||
|
"platform": "ios",
|
||||||
|
"size": "1024x1024"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"appearances": [
|
||||||
|
{
|
||||||
|
"appearance": "luminosity",
|
||||||
|
"value": "tinted"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"filename": "AppIcon-tinted.png",
|
||||||
|
"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-NotificationCenter-66.png",
|
||||||
|
"idiom": "watch",
|
||||||
|
"role": "notificationCenter",
|
||||||
|
"scale": "2x",
|
||||||
|
"size": "33x33",
|
||||||
|
"subtype": "45mm"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"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-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"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info": {
|
||||||
|
"author": "xcode",
|
||||||
|
"version": 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 7.9 KiB |
|
After Width: | Height: | Size: 9.1 KiB |
|
After Width: | Height: | Size: 9.6 KiB |
|
After Width: | Height: | Size: 5.3 KiB |
|
After Width: | Height: | Size: 9.0 KiB |
|
After Width: | Height: | Size: 747 KiB |
|
After Width: | Height: | Size: 4.2 KiB |
|
After Width: | Height: | Size: 5.0 KiB |
|
After Width: | Height: | Size: 6.3 KiB |
|
After Width: | Height: | Size: 25 KiB |
|
After Width: | Height: | Size: 30 KiB |
|
After Width: | Height: | Size: 35 KiB |
|
After Width: | Height: | Size: 42 KiB |
|
After Width: | Height: | Size: 46 KiB |
|
After Width: | Height: | Size: 747 KiB |
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "AppIconArtwork.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 747 KiB |
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "AppIconPreview.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 208 KiB |
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "AppIconPreviewDark.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
},
|
||||||
|
"data" : [
|
||||||
|
{
|
||||||
|
"filename" : "fuelboard_dump.json",
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "PreviewWallpaper.jpg",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 177 KiB |
@@ -1,9 +1,14 @@
|
|||||||
{
|
{
|
||||||
"images": [
|
"images": [
|
||||||
{
|
{
|
||||||
"filename": "brand_applegreen.png",
|
"filename": "brand_applegreen_2x.png",
|
||||||
"idiom": "universal",
|
"idiom": "universal",
|
||||||
"scale": "2x"
|
"scale": "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "brand_applegreen_3x.png",
|
||||||
|
"idiom": "universal",
|
||||||
|
"scale": "3x"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"info": {
|
"info": {
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 3.4 KiB |
|
After Width: | Height: | Size: 5.1 KiB |
@@ -1,9 +1,14 @@
|
|||||||
{
|
{
|
||||||
"images": [
|
"images": [
|
||||||
{
|
{
|
||||||
"filename": "brand_asda.png",
|
"filename": "brand_asda_2x.png",
|
||||||
"idiom": "universal",
|
"idiom": "universal",
|
||||||
"scale": "2x"
|
"scale": "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "brand_asda_3x.png",
|
||||||
|
"idiom": "universal",
|
||||||
|
"scale": "3x"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"info": {
|
"info": {
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 8.2 KiB |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 2.0 KiB |
@@ -1,9 +1,14 @@
|
|||||||
{
|
{
|
||||||
"images": [
|
"images": [
|
||||||
{
|
{
|
||||||
"filename": "brand_bp.png",
|
"filename": "brand_bp_2x.png",
|
||||||
"idiom": "universal",
|
"idiom": "universal",
|
||||||
"scale": "2x"
|
"scale": "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "brand_bp_3x.png",
|
||||||
|
"idiom": "universal",
|
||||||
|
"scale": "3x"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"info": {
|
"info": {
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 150 KiB |
|
After Width: | Height: | Size: 6.0 KiB |
|
After Width: | Height: | Size: 8.2 KiB |
@@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"images": [
|
||||||
|
{
|
||||||
|
"filename": "brand_circle_k_2x.png",
|
||||||
|
"idiom": "universal",
|
||||||
|
"scale": "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "brand_circle_k_3x.png",
|
||||||
|
"idiom": "universal",
|
||||||
|
"scale": "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info": {
|
||||||
|
"author": "xcode",
|
||||||
|
"version": 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 2.8 KiB |
|
After Width: | Height: | Size: 4.7 KiB |
@@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"images": [
|
||||||
|
{
|
||||||
|
"filename": "brand_essar_2x.png",
|
||||||
|
"idiom": "universal",
|
||||||
|
"scale": "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "brand_essar_3x.png",
|
||||||
|
"idiom": "universal",
|
||||||
|
"scale": "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info": {
|
||||||
|
"author": "xcode",
|
||||||
|
"version": 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 2.4 KiB |
|
After Width: | Height: | Size: 3.8 KiB |
@@ -1,9 +1,14 @@
|
|||||||
{
|
{
|
||||||
"images": [
|
"images": [
|
||||||
{
|
{
|
||||||
"filename": "brand_esso.png",
|
"filename": "brand_esso_2x.png",
|
||||||
"idiom": "universal",
|
"idiom": "universal",
|
||||||
"scale": "2x"
|
"scale": "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "brand_esso_3x.png",
|
||||||
|
"idiom": "universal",
|
||||||
|
"scale": "3x"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"info": {
|
"info": {
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 8.1 KiB |
|
After Width: | Height: | Size: 3.4 KiB |
|
After Width: | Height: | Size: 5.8 KiB |
@@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"images": [
|
||||||
|
{
|
||||||
|
"filename": "brand_gleaner_2x.png",
|
||||||
|
"idiom": "universal",
|
||||||
|
"scale": "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "brand_gleaner_3x.png",
|
||||||
|
"idiom": "universal",
|
||||||
|
"scale": "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info": {
|
||||||
|
"author": "xcode",
|
||||||
|
"version": 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 2.6 KiB |
|
After Width: | Height: | Size: 5.4 KiB |
@@ -1,9 +1,14 @@
|
|||||||
{
|
{
|
||||||
"images": [
|
"images": [
|
||||||
{
|
{
|
||||||
"filename": "brand_gulf.png",
|
"filename": "brand_gulf_2x.png",
|
||||||
"idiom": "universal",
|
"idiom": "universal",
|
||||||
"scale": "2x"
|
"scale": "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "brand_gulf_3x.png",
|
||||||
|
"idiom": "universal",
|
||||||
|
"scale": "3x"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"info": {
|
"info": {
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 30 KiB |
|
After Width: | Height: | Size: 3.6 KiB |
|
After Width: | Height: | Size: 5.5 KiB |
@@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"images": [
|
||||||
|
{
|
||||||
|
"filename": "brand_highland_fuels_2x.png",
|
||||||
|
"idiom": "universal",
|
||||||
|
"scale": "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "brand_highland_fuels_3x.png",
|
||||||
|
"idiom": "universal",
|
||||||
|
"scale": "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info": {
|
||||||
|
"author": "xcode",
|
||||||
|
"version": 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 636 B |
|
After Width: | Height: | Size: 970 B |
@@ -1,9 +1,14 @@
|
|||||||
{
|
{
|
||||||
"images": [
|
"images": [
|
||||||
{
|
{
|
||||||
"filename": "brand_jet.png",
|
"filename": "brand_jet_2x.png",
|
||||||
"idiom": "universal",
|
"idiom": "universal",
|
||||||
"scale": "2x"
|
"scale": "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "brand_jet_3x.png",
|
||||||
|
"idiom": "universal",
|
||||||
|
"scale": "3x"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"info": {
|
"info": {
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 8.8 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
@@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"images": [
|
||||||
|
{
|
||||||
|
"filename": "brand_maxol_2x.png",
|
||||||
|
"idiom": "universal",
|
||||||
|
"scale": "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "brand_maxol_3x.png",
|
||||||
|
"idiom": "universal",
|
||||||
|
"scale": "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info": {
|
||||||
|
"author": "xcode",
|
||||||
|
"version": 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 4.2 KiB |
|
After Width: | Height: | Size: 7.8 KiB |
@@ -1,9 +1,14 @@
|
|||||||
{
|
{
|
||||||
"images": [
|
"images": [
|
||||||
{
|
{
|
||||||
"filename": "brand_morrisons.png",
|
"filename": "brand_morrisons_2x.png",
|
||||||
"idiom": "universal",
|
"idiom": "universal",
|
||||||
"scale": "2x"
|
"scale": "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "brand_morrisons_3x.png",
|
||||||
|
"idiom": "universal",
|
||||||
|
"scale": "3x"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"info": {
|
"info": {
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 2.4 KiB |
@@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"images": [
|
||||||
|
{
|
||||||
|
"filename": "brand_murco_2x.png",
|
||||||
|
"idiom": "universal",
|
||||||
|
"scale": "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "brand_murco_3x.png",
|
||||||
|
"idiom": "universal",
|
||||||
|
"scale": "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info": {
|
||||||
|
"author": "xcode",
|
||||||
|
"version": 1
|
||||||
|
}
|
||||||
|
}
|
||||||