From e04e97fb66653f51298df6b538dfef1ab283c0ed Mon Sep 17 00:00:00 2001 From: Ilia Denisov Date: Wed, 15 Jul 2026 19:24:45 +0200 Subject: [PATCH] docs(apple): drop [FIXED] tags from the plan Decisions stay recorded; the tag implied false permanence. Open items keep their [OPEN / TRACKED] / [GATE] markers. --- apple/PLAN.md | 53 +++++++++++++++++++++++---------------------------- 1 file changed, 24 insertions(+), 29 deletions(-) diff --git a/apple/PLAN.md b/apple/PLAN.md index 36d6b1c..5ad1e9c 100644 --- a/apple/PLAN.md +++ b/apple/PLAN.md @@ -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