Files
workouts/REQUIREMENTS.md
T
rzen 85d0eaddbb Workouts 2.0: re-base persistence on iCloud Drive documents
Replace Core Data + NSPersistentCloudKitContainer + App-Group store +
WatchConnectivity dictionary sync with the QuickRabbit iCloud-documents
architecture:

- iCloud Drive JSON documents are the sole source of truth (one file per
  aggregate: Splits/<ULID>.json, Workouts/YYYY/MM/<ULID>.json), with a
  rebuildable SwiftData cache populated only by an NSMetadataQuery observer
  and a connect-time reconcile. Soft-delete tombstones; hard iCloud gate.
- Shared model layer (ULID, Codable *Documents + stateless mappers, @Model
  cache entities, SwiftData container) compiled into both targets.
- New iPhone<->Watch bridge over WatchConnectivity keyed by ULIDs; the phone
  is the sole writer of iCloud Drive, the watch round-trips documents.
- AppServices DI + iCloud-required root gate; Swift 6 strict concurrency.
- Starter splits generated on demand from the bundled YAML catalogs.
- Migrate to XcodeGen (project.yml), iOS 26 / watchOS 26; CloudDocuments
  entitlement (drop CloudKit/App Group/aps-environment).
- Duration stored as Int seconds (was a Date epoch hack); fix workout
  end-on-create, undismissable delete dialog, toolbar-hiding nav stacks,
  and the Settings placeholder.
- Add README/CHANGELOG/LICENSE, .gitignore, refreshed REQUIREMENTS, and the
  Scripts/ TestFlight pipeline (release.sh + ASC API scripts).

MARKETING_VERSION 2.0.
2026-06-19 14:25:27 -04:00

73 lines
3.5 KiB
Markdown

# 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/<ULID>.json` — a `SplitDocument` embedding its `[ExerciseDocument]`
- `Workouts/YYYY/MM/<ULID>.json` — a `WorkoutDocument` embedding its `[WorkoutLogDocument]`
- `Stubs/<ULID>.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).
## 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: run workouts from the wrist; 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.