Files
workouts/REQUIREMENTS.md
T
rzen d5915a9552 Add HIIT watch runner, rest-time setting, and HealthKit watch auto-launch
- Redesign the watch app into an active-workout runner: a root gate shows the
  in-progress workout's exercises or prompts to start one on iPhone, and each
  exercise runs as a horizontally-paged HIIT cycle (count-up work, count-down
  rest with final-three-second haptics + auto-advance, One More / Done on the
  last set). Replaces the old history list.
- Add a configurable rest-between-sets duration in iPhone Settings (default 45s),
  synced to the watch over WatchConnectivity.
- Launch the watch app into the session when a workout starts on the phone via
  HealthKit (startWatchApp); the watch runs an HKWorkoutSession for foreground
  runtime and ends it when the workout finishes. Adds the HealthKit entitlement +
  Health usage strings on both targets and WKBackgroundModes on the watch.

Claude-Session: https://claude.ai/code/session_018gg69MaUetDNzWzBXisfMV
2026-06-19 16:16:44 -04:00

3.9 KiB

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).
  • 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.