Starter splits ship as byte-canonical SplitDocument JSON with fixed ULIDs (Workouts/Resources/StarterSplits, regenerated by Scripts/generate_starter_splits.swift) and auto-seed after connect into a verifiably empty container, re-checked after a settle delay — wrong guesses are harmless because identical bytes make same-path conflicts empty and tombstones reap resurrected seeds. Seeds are immutable: SyncEngine.save(split:) forks an edited seed to a fresh ULID and soft-deletes the original, whose stub is exempt from pruning (IndieSync 0.3.0 prune(exempting:)) and vetoes resurrection forever; split views resolve by id through a redirect map to follow the swap. Add Starter Splits in Settings restores deleted seeds by lifting the veto stub and rewriting the bundle bytes. Also fixes ingestFromWatch bypassing the tombstone veto (a phone-deleted workout resurrected when a stale watch resent it) and reaps a live file immediately when its tombstone arrives. SplitDetailView also picks up the category-grouped exercise sections from the exercise-category work.
76 lines
4.3 KiB
Markdown
76 lines
4.3 KiB
Markdown
# Workouts
|
|
|
|
A workout tracking app for iPhone and Apple Watch. Build workout splits, run
|
|
sessions, and track your progress — with your data stored as plain JSON files in
|
|
your own iCloud Drive.
|
|
|
|
## Key Features
|
|
|
|
- **Workout splits** — organize exercises into reusable routines with custom
|
|
colors, icons, and an activity type (strength, HIIT, cardio, …). Each exercise
|
|
is tagged as **warm-up** or **main circuit**, and the split screen groups its
|
|
list accordingly. Built-in starter splits (Upper Body / Core / Lower Body
|
|
machine routines, plus an equipment-free **Bodyweight Core** circuit with its
|
|
own warm-up) appear automatically on first launch; editing one makes it your
|
|
own copy, and deleted starters can be restored from Settings.
|
|
- **Exercise library** — a bundled catalog of starter exercises (bodyweight and
|
|
machine-based) to populate your splits, plus a growing reference library
|
|
(`Exercise Library/`) with per-exercise setup, cues, mistakes, progressions,
|
|
and a rig-animated stick-figure visual system.
|
|
- **Run a workout** — start a session from a split, then tap an exercise to run it
|
|
as a paged flow: a **Ready?** lead-in, count-up work phases, count-down rests, and
|
|
a **Finish** page — mirroring the Apple Watch. Swipe a row to mark it complete, or
|
|
swipe to edit its plan (sets/reps/weight or duration) and notes. End a workout early
|
|
from the **⋯** menu — **Save** keeps your progress (remaining exercises are marked
|
|
skipped) or **Discard** removes it.
|
|
- **Animated form guide** — exercises with a bundled motion (the bodyweight core
|
|
moves, e.g. Bird Dog) show a looping stick-figure demonstration on the run
|
|
screen's bottom half, rendered live from the Exercise Library's rig data with
|
|
the working limbs highlighted.
|
|
- **Progress tracking** — weight-progression charts per exercise across past
|
|
sessions.
|
|
- **Apple Watch companion** — starting a workout on the iPhone launches the watch
|
|
app straight into it. The watch lists your in-progress workouts; pick one, pick an
|
|
exercise, and run it as a paged flow: a **Ready?** lead-in, count-up work phases,
|
|
count-down rests with final-three-second haptics and auto-advance, and a **Finish**
|
|
page with **One More** and a **Done** that auto-completes after a countdown. A
|
|
phase-dot row (purple work, teal rest) tracks progress. Rest time and the
|
|
auto-finish countdown are configurable; changes sync back to the phone.
|
|
- **Two-way live run** — prop your iPhone up during an Apple Watch workout and it runs
|
|
the same Ready → work/rest → Finish flow with live timers, in step with the watch. It's
|
|
bidirectional: drive from either device — swipe ahead, finish a set, add one — and the
|
|
other follows. Only *human* transitions are sent; automatic ones (a rest timer ending)
|
|
advance both devices independently off shared start times, so they never fight.
|
|
- **Watch face complication** — a launcher complication you can place on any Apple
|
|
Watch face; tap it to open the app. Available in the circular, corner, inline, and
|
|
rectangular accessory slots.
|
|
- **Apple Health & Activity rings** — completed workouts are saved to Apple Health
|
|
and credit your Move and Exercise rings: recorded live on the Apple Watch (heart
|
|
rate + active energy), or estimated on the iPhone when you train without your
|
|
watch. See live heart rate and calories on the watch, and a post-workout summary
|
|
(duration, calories, avg/max heart rate, heart-rate zones, total volume) you can
|
|
revisit on any past workout. Weights display in your choice of pounds or kilograms.
|
|
- **iCloud Drive sync** — your data lives as human-readable JSON in your iCloud
|
|
Drive, synced across devices and visible in the Files app. iCloud is required.
|
|
|
|
## Architecture
|
|
|
|
iCloud Drive JSON documents are the **sole source of truth**; a local SwiftData
|
|
store is a rebuildable read-through cache populated exclusively by an
|
|
`NSMetadataQuery` observer (one-way flow: files → observer → cache). The phone is
|
|
the only device that touches iCloud Drive; the Apple Watch is a thin remote that
|
|
round-trips workout changes through the phone via WatchConnectivity.
|
|
|
|
See `REQUIREMENTS.md` for the data model and `CLAUDE.md` for project guidance.
|
|
|
|
## Building
|
|
|
|
The Xcode project is generated with [XcodeGen](https://github.com/yonaskolb/XcodeGen):
|
|
|
|
```sh
|
|
xcodegen generate
|
|
open Workouts.xcodeproj
|
|
```
|
|
|
|
Requires Xcode 26 (iOS 26 / watchOS 26, Swift 6).
|