85d0eaddbb
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.
3.5 KiB
3.5 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— aSplitDocumentembedding its[ExerciseDocument]Workouts/YYYY/MM/<ULID>.json— aWorkoutDocumentembedding 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
NSMetadataQueryobserver (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.