docs(apple): drop [FIXED] tags from the plan
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Has been skipped
CI / integration (pull_request) Has been skipped
CI / ui (pull_request) Has been skipped
CI / conformance (pull_request) Has been skipped
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Has been skipped
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Has been skipped
CI / integration (pull_request) Has been skipped
CI / ui (pull_request) Has been skipped
CI / conformance (pull_request) Has been skipped
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Has been skipped
Decisions stay recorded; the tag implied false permanence. Open items keep their [OPEN / TRACKED] / [GATE] markers.
This commit is contained in:
+24
-29
@@ -4,8 +4,8 @@ Native **SwiftUI** application for iPhone and iPad, written **from scratch** (no
|
||||
wrapper, unlike the Android app under `ui/android`). This document is the **source of truth and
|
||||
continuity** for the effort: it lives in the repo so it travels to whatever machine does the work —
|
||||
in particular the **Mac Claude session** that does the actual building. Read it first; append to it
|
||||
as decisions land. Nothing here is final until built and verified; where a decision is locked it is
|
||||
marked **[FIXED]**.
|
||||
as decisions land. Nothing here is final until built and verified; any decision can be reopened.
|
||||
Items still open carry an **[OPEN / TRACKED]** or **[GATE]** marker.
|
||||
|
||||
Chat with the owner is in Russian per the user-level guide; this doc, like all repo docs, is in
|
||||
**English**.
|
||||
@@ -62,8 +62,7 @@ simulator installed generally; the steps below are mostly **verify**, a few are
|
||||
|
||||
---
|
||||
|
||||
## 2. Fixed decisions [FIXED]
|
||||
|
||||
## 2. Fixed decisions
|
||||
| Topic | Decision |
|
||||
|---|---|
|
||||
| Language / UI | Native **SwiftUI**, from scratch; **not** Capacitor. |
|
||||
@@ -125,8 +124,7 @@ custom build phases, so custom needs are expressible; worst case escape hatches
|
||||
|
||||
---
|
||||
|
||||
## 5. Determining optimization principles [FIXED]
|
||||
|
||||
## 5. Determining optimization principles
|
||||
These five shape the architecture from the start (not "add later"):
|
||||
|
||||
1. **Push (APNs) instead of a live socket while backgrounded.** For a turn-based game we do **not** hold
|
||||
@@ -159,8 +157,7 @@ These five shape the architecture from the start (not "add later"):
|
||||
|
||||
---
|
||||
|
||||
## 7. Storage — persist eagerly, do not trust `willTerminate` [FIXED]
|
||||
|
||||
## 7. Storage — persist eagerly, do not trust `willTerminate`
|
||||
There is **no reliable destructive callback** on iOS: `willTerminate` is often **not** called (swipe
|
||||
force-kill, memory kill happen silently). Relying on catching a "destroy" event is a trap.
|
||||
|
||||
@@ -191,8 +188,7 @@ there — they need the **codec unit tests**.
|
||||
|
||||
---
|
||||
|
||||
## 9. Crash reporting & analytics [FIXED]
|
||||
|
||||
## 9. Crash reporting & analytics
|
||||
- **Crashes: MetricKit.** `MXMetricManager` delivers crash/hang/CPU/disk diagnostics to the app; we
|
||||
**upload the payload to our own backend** and view it in the existing **Grafana/logs** contour — fully
|
||||
self-hosted, zero third-party, no paid service. Apple's Xcode Organizer / App Store Connect also provides
|
||||
@@ -354,8 +350,7 @@ with the game screen (the vertical slice).
|
||||
· `ShareLink` / share sheet (export) · **TipKit** (coachmarks) · grouped `List` (settings/legal) · SIWA
|
||||
native sheet (auth).
|
||||
|
||||
### Assets, tiles & icons [FIXED]
|
||||
- **Tiles are drawn as runtime vectors** (a rounded rect + a font glyph + the point value, on the board
|
||||
### Assets, tiles & icons- **Tiles are drawn as runtime vectors** (a rounded rect + a font glyph + the point value, on the board
|
||||
`Canvas`) — **no image assets, no pre-generation, no local-store cache.** Resolution-independent (crisp
|
||||
under zoom-on-drop), theme/variant/locale-adaptive for free. Cache rendered tile images
|
||||
(`ImageRenderer`) **only if** profiling later shows a need. The crossword loading splash **reuses the
|
||||
@@ -374,14 +369,14 @@ native sheet (auth).
|
||||
|
||||
#### F. Game screen — detail
|
||||
|
||||
**Layout [FIXED]**
|
||||
**Layout**
|
||||
- **iPhone: full board always visible + zoom-on-drop** (hybrid, web-parity, setting-gated by the
|
||||
existing "Zoom the board" toggle). Board scaled to width; on tile drop it magnifies toward the drop
|
||||
point for precision.
|
||||
- **Bottom-anchored stack**, top→bottom: *(top strip — the ad banner (L) + connection chrome, else
|
||||
empty)* → **seats header** → **board** → **rack** → **bottom toolbar**. Everything is packed to the
|
||||
bottom; the top strip hosts the L banner / Connecting…/Offline/connection-lost chrome, else empty.
|
||||
- **Orientation [FIXED]: iPhone portrait-only; iPad portrait + landscape.**
|
||||
- **Orientation: iPhone portrait-only; iPad portrait + landscape.**
|
||||
|
||||
**Four regions**
|
||||
1. **Scoreboard / header** — seats (name, score, whose-turn), unread dot (red msg / amber nudge),
|
||||
@@ -426,7 +421,7 @@ native sheet (auth).
|
||||
resign/add-friend/block/chat hidden; a started move stays as a draft); if already in chat/dictionary
|
||||
you stay (chat read-only, dictionary keeps checking on-device).
|
||||
|
||||
**UX decisions [FIXED]**
|
||||
**UX decisions**
|
||||
- **Comms sheet:** a **single detented sheet** with a segmented control **[History | Chat |
|
||||
Dictionary]**; the board + seats header stay visible above; native swipe-to-dismiss + grabber.
|
||||
**Opened by tapping the seats header** (where the unread dot / 💬 live). iPad landscape later adapts
|
||||
@@ -464,7 +459,7 @@ emerge while implementing the vertical slice, step 3.)*
|
||||
|
||||
#### D. Lobby — detail
|
||||
|
||||
**UX decisions [FIXED]**
|
||||
**UX decisions**
|
||||
- **List style:** native **inset-grouped `List`** (the Settings-app look — floating rounded section
|
||||
cards over a grouped background). Sections: **your turn / opponent's turn / finished** (empty hidden).
|
||||
- **Grouping (option A — unified lobby):** one lobby, all games in the three state sections. **Local
|
||||
@@ -499,7 +494,7 @@ emerge while implementing the vertical slice, step 3.)*
|
||||
|
||||
#### E. New Game — detail
|
||||
|
||||
**UX decisions [FIXED]**
|
||||
**UX decisions**
|
||||
- **Structure:** a top **segmented `[Quick game | With friends]`** + a contextual **inset-grouped form**
|
||||
below (consistent with the lobby aesthetic).
|
||||
- *Quick game:* opponent segmented **`[AI | Random]`** → variant (emblem chips) → RU "multiple words"
|
||||
@@ -525,7 +520,7 @@ emerge while implementing the vertical slice, step 3.)*
|
||||
multiple-words · **move timeout** (5 min–24 h, default 24 h) · **hints** (allowed? how many each) ·
|
||||
**leaver's-tile disposition** (returned to bag / removed — 3–4-player games only).
|
||||
|
||||
**UX decisions [FIXED] (sub-details)**
|
||||
**UX decisions (sub-details)**
|
||||
- **Cap 🔒 prompt:** a 🔒 badge on the **Start** control (looks unavailable); a tap opens a native
|
||||
alert — guest: a "Sign in / Create account" action; signed-in: an informational "finish a current
|
||||
game first."
|
||||
@@ -540,7 +535,7 @@ emerge while implementing the vertical slice, step 3.)*
|
||||
|
||||
#### C. Login / identity — detail (v1: SIWA + email + guest)
|
||||
|
||||
**UX decisions [FIXED]**
|
||||
**UX decisions**
|
||||
- **No hard login wall — guest by default** (offline-first, as Android native). First launch drops
|
||||
straight into the lobby as a guest.
|
||||
- **Reusable sign-in sheet:** a bottom **detented sheet** (same idiom as the comms sheet) with the
|
||||
@@ -557,7 +552,7 @@ emerge while implementing the vertical slice, step 3.)*
|
||||
|
||||
#### B. Onboarding — detail (TipKit, native)
|
||||
|
||||
**UX decisions [FIXED]**
|
||||
**UX decisions**
|
||||
- **TipKit, native flavour** (confirmed): contextual tips anchored to controls, ordered via **`TipGroup`**
|
||||
— **no full-screen dimming, no tap-anywhere-advance**; each tip dismisses itself (its ✕ or performing
|
||||
the action). Shown once; per-device eligibility.
|
||||
@@ -568,7 +563,7 @@ emerge while implementing the vertical slice, step 3.)*
|
||||
|
||||
#### G. Friends — detail (Settings → Friends)
|
||||
|
||||
**UX decisions [FIXED]**
|
||||
**UX decisions**
|
||||
- **Structure:** a single **inset-grouped `List`** with sections **Requests** (incoming: accept /
|
||||
decline; outgoing: cancel) · **Friends** · **Blocked** (unblock). A toolbar **"+"** opens the add
|
||||
sheet.
|
||||
@@ -587,7 +582,7 @@ emerge while implementing the vertical slice, step 3.)*
|
||||
|
||||
#### H. Profile & settings — detail
|
||||
|
||||
**UX decisions [FIXED]**
|
||||
**UX decisions**
|
||||
- **Settings hub** = an icon **`List` of drill-down `NavigationLink` rows** (the Podcasts → Library
|
||||
pattern), SF-symbol icons, with room left at the bottom for optional general info later. Rows:
|
||||
- **Interface** (⚙️) → display-settings screen.
|
||||
@@ -602,12 +597,12 @@ emerge while implementing the vertical slice, step 3.)*
|
||||
- **Interface screen** (display prefs): language · theme · board bonus-label style · reduce-motion ·
|
||||
zoom-the-board.
|
||||
|
||||
**Guest has no editable settings** [FIXED] — the random nick is not changeable, the variant is always
|
||||
**Guest has no editable settings** — the random nick is not changeable, the variant is always
|
||||
`erudite_ru`, the timezone is auto-detected (never set by hand). So there is **no guest profile
|
||||
screen**: the profile icon for a guest opens the **sign-in sheet** (confirms C). It also confirms E's
|
||||
"variant picker hidden when a single variant is enabled" for guests.
|
||||
|
||||
**Profile screen (signed-in / durable)** [FIXED] — an inset-grouped `Form`:
|
||||
**Profile screen (signed-in / durable)** — an inset-grouped `Form`:
|
||||
- **Name** — inline `TextField` + validation (letters + `·`/`.`/`_` separator, optional trailing `.`
|
||||
or ≤5 digits, ≤32 chars, ≤5 specials).
|
||||
- **Play** — timezone (UTC-offset picker, pre-filled from the device) · away window (10-min grid, ≤12 h,
|
||||
@@ -622,7 +617,7 @@ screen**: the profile icon for a guest opens the **sign-in sheet** (confirms C).
|
||||
|
||||
#### I. Wallet & store — detail (durable only; delivery staged)
|
||||
|
||||
**UX decisions [FIXED]**
|
||||
**UX decisions**
|
||||
- **Structure:** inset-grouped — a **balance card** (running-context «Фишка» first, then linked
|
||||
other-platform chips behind their logos: VK / Telegram / web / **Apple** as a new context) → **Active**
|
||||
line (hints remaining + ad-free end date / "forever"; hidden when empty) → segmented **`[Buy | Spend]`**
|
||||
@@ -644,7 +639,7 @@ screen**: the profile icon for a guest opens the **sign-in sheet** (confirms C).
|
||||
|
||||
#### J. Feedback — detail (durable only; guests hidden)
|
||||
|
||||
**UX decisions [FIXED]**
|
||||
**UX decisions**
|
||||
- inset-grouped `Form`: **`TextEditor`** (≤1024, live counter) → attachment row → **Send**.
|
||||
- **Attachment:** one file ≤1 MB (images / PDF / text-log / office / RTF / archives). **"Attach"** →
|
||||
menu **`PhotosPicker` (Photos)** / **`fileImporter` (Files)**; **no camera**. An unsupported file is
|
||||
@@ -658,7 +653,7 @@ screen**: the profile icon for a guest opens the **sign-in sheet** (confirms C).
|
||||
|
||||
#### K. Info / legal — detail
|
||||
|
||||
**UX decisions [FIXED]**
|
||||
**UX decisions**
|
||||
- **About screen** (Info hub row): app name · **version** (native build + the client-version) · links to
|
||||
the three legal docs · copyright; optional "Rate on the App Store".
|
||||
- **Legal docs** (EULA `/eula/`, privacy `/privacy/`, offer `/offer/` + live price list): open via
|
||||
@@ -668,7 +663,7 @@ screen**: the profile icon for a guest opens the **sign-in sheet** (confirms C).
|
||||
|
||||
#### A. Boot / launch & update — detail
|
||||
|
||||
**UX decisions [FIXED]**
|
||||
**UX decisions**
|
||||
- **LaunchScreen:** static, OS-level, instant (**iOS cannot animate the launch screen**) — brand/logo.
|
||||
- **In-app loading:** the crossword-tiles splash (ЭРУДИТ/ЗАГРУЗКА/ОЖИДАНИЕ) shows **only while the lobby
|
||||
list isn't ready**; fast silent boot → usually straight to the lobby. Offline cold launch → straight to
|
||||
@@ -684,7 +679,7 @@ screen**: the profile icon for a guest opens the **sign-in sheet** (confirms C).
|
||||
|
||||
#### L. Advertising banner — detail (cross-cutting component)
|
||||
|
||||
**UX decisions [FIXED]**
|
||||
**UX decisions**
|
||||
- **Placement:** a one-line strip **on the main tabs (Lobby / New Game / Statistics) AND in-game (F)** —
|
||||
players spend most time in-game. Hidden in settings drill-downs and modal flows. **In-game it occupies
|
||||
the top strip** (the empty space above the seats header), coexisting with the connection chrome
|
||||
|
||||
Reference in New Issue
Block a user