# Workouts App Requirements ## Overview A workout tracking iOS app with an Apple Watch companion that helps users manage workout splits, run sessions, and track progress. Data is stored as JSON documents in the user's iCloud Drive and synced across devices. ## Platform Requirements - iOS app (iPhone) — iOS 26+ - watchOS app (Apple Watch companion) — watchOS 26+ - Swift 6, strict concurrency; SwiftUI throughout - Project generated with XcodeGen (`project.yml`) ## Persistence Architecture - **iCloud Drive JSON documents are the sole source of truth.** One JSON file per aggregate root under the app's iCloud container `Documents/`: - `Splits/.json` — a `SplitDocument` embedding its `[ExerciseDocument]` - `Workouts/YYYY/MM/.json` — a `WorkoutDocument` embedding its `[WorkoutLogDocument]` - `Stubs/.json` — soft-delete tombstones (30-day GC) - **SwiftData is a rebuildable read-through cache.** App code never writes the cache directly: every save/delete writes a file; an `NSMetadataQuery` observer (and a connect-time reconcile) is the sole cache mutator. The cache is wiped and rebuilt on schema-version bump or iCloud-account change. - **No CloudKit.** Container: `iCloud.dev.rzen.indie.Workouts` (CloudDocuments). - **iCloud is required** — without it the app shows a blocking "iCloud Required" gate (no local-only fallback). ## Data Model - **Split**: `id` (ULID), `name`, `color`, `systemImage`, `order`, timestamps; embeds Exercises. - **Exercise**: `id`, `name`, `order`, `sets`, `reps`, `weight`, `loadType` (none/weight/duration), `durationTotalSeconds`, `weightLastUpdated`, `weightReminderTimeIntervalWeeks`. - **Workout**: `id` (ULID), `splitID`/`splitName` (denormalized), `start`, `end?`, `status` (notStarted/inProgress/completed/skipped), timestamps; embeds logs. - **WorkoutLog**: `id`, `exerciseName`, `order`, `sets`, `reps`, `weight`, `loadType`, `durationTotalSeconds`, `currentStateIndex`, `completed`, `status`, `notes?`, `date`. Identity is a ULID string (stable across cache rebuilds). Duration is stored as integer seconds. ## Apple Watch The phone is the only device that touches iCloud Drive. The watch keeps its own local SwiftData cache fed only by WatchConnectivity: - Phone → Watch: pushes all splits + recent workouts (application context) on every cache change. - Watch → Phone: sends an updated `WorkoutDocument` (keyed by ULID); the phone applies it through the file write path (the sole writer of iCloud Drive). - Starting a workout on the phone launches the watch app into it via HealthKit (`startWatchApp(toHandle:)`); the watch runs an `HKWorkoutSession` for foreground runtime and ends it when no active workout remains. Requires the HealthKit capability and a one-time Health permission on both devices. ## Seed Data Starter splits (Upper Body / Core / Lower Body) are generated on demand from the bundled YAML exercise catalogs (`Workouts/Resources/*.exercises.yaml`) — never auto-seeded. The catalogs also back the in-workout exercise picker. ## Features - Create/edit/delete/reorder workout splits with custom colors and SF Symbol icons. - Add exercises to splits (from the bundled catalog or manually); set default sets/reps/weight or a duration. - Start a workout from a split; track per-exercise set/rest/done progress; mark complete/skipped; edit the plan during a session (mirrored back to the split). - Weight-progression charts per exercise across past sessions. - Apple Watch: starting a workout on the phone launches the watch into it; run the session from the wrist (HIIT-style work/rest cycles); changes sync back to the phone. ## Dependencies - **Yams** — YAML parsing for the exercise catalogs. - **IndieAbout** — About section in Settings (bundles LICENSE.md / CHANGELOG.md). ## Release - TestFlight / App Store via `Scripts/release.sh` (xcodebuild archive + App Store Connect API upload; credentials in `.env.release`). The iOS archive embeds the watch app.