Files
workouts/project.yml
T
rzen cbdf02bca7 Add the macOS app: a library-management companion (routines, schedules, exercise browser)
A new 'Workouts Mac' target (same bundle ID as iOS, universal purchase)
with a NavigationSplitView shell over the shared data/sync layers:
routine management with starter gallery and seed-fork follow, schedule
editing (reminders stay iPhone-scheduled), and a browse-only exercise
library with the animated figures and reference guides.

Multi-writer stance: the Mac writes only routine and schedule documents
(whole-document last-writer-wins) and never workout documents, whose
per-log merge exists only on the watch ingest path. The Mac reconciles
on window activation (throttled) since iCloud syncs while the app is
closed, and flushes pending writes on deactivation.
2026-07-16 19:55:06 -04:00

237 lines
7.9 KiB
YAML

name: Workouts
options:
bundleIdPrefix: dev.rzen.indie
deploymentTarget:
iOS: "26.0"
watchOS: "26.0"
macOS: "26.0"
xcodeVersion: "26.0"
defaultConfig: Debug
settings:
base:
SWIFT_VERSION: "6.0"
DEVELOPMENT_TEAM: ${APPLE_TEAM_ID}
MARKETING_VERSION: "2.3"
CURRENT_PROJECT_VERSION: "1"
ENABLE_USER_SCRIPT_SANDBOXING: "NO"
DEAD_CODE_STRIPPING: "YES"
packages:
IndieAbout:
url: https://git.rzen.dev/rzen/indie-about.git
from: "0.2.2"
IndieSync:
url: https://git.rzen.dev/rzen/indie-sync.git
from: "0.3.0"
IndieBackup:
url: https://git.rzen.dev/rzen/indie-backup.git
from: "2.0.0"
# Shared post-build phase — stamps CFBundleVersion (git commit count), BuildDate, and
# BuildHash into each target's (and dSYM's) Info.plist. Aliased into every code-bearing
# target below so the app, watch app, and both widget extensions stamp identical metadata.
scriptAnchors:
updateBuildInfo: &updateBuildInfo
- script: '"${SRCROOT}/../indie-skills/skills/app-versioning/scripts/update_build_info.sh"'
name: Update Build Info
shell: /bin/sh
basedOnDependencyAnalysis: false
inputFiles:
- $(TARGET_BUILD_DIR)/$(INFOPLIST_PATH)
- $(DWARF_DSYM_FOLDER_PATH)/$(DWARF_DSYM_FILE_NAME)/Contents/Info.plist
targets:
# ---- iOS app (owns iCloud Drive sync; embeds the watch app) ----------------
Workouts:
type: application
platform: iOS
sources:
- path: Shared
- path: Workouts
excludes:
- "Resources/Info-*.plist"
- "Resources/*.entitlements"
- path: CHANGELOG.md
buildPhase: resources
type: file
- path: README.md
buildPhase: resources
type: file
- path: LICENSE.md
buildPhase: resources
type: file
# The Live Activity attributes are shared with the widget extension (which owns the
# folder); the app compiles just this one file so it can start / update / end the activity.
- path: "Workouts Widget/WorkoutActivityAttributes.swift"
dependencies:
- package: IndieAbout
- package: IndieSync
- package: IndieBackup
- target: Workouts Watch App
- target: Workouts Widget
embed: true
postBuildScripts: *updateBuildInfo
settings:
base:
PRODUCT_BUNDLE_IDENTIFIER: dev.rzen.indie.Workouts
INFOPLIST_FILE: Workouts/Resources/Info-iOS.plist
CODE_SIGN_ENTITLEMENTS: Workouts/Resources/Workouts-iOS.entitlements
GENERATE_INFOPLIST_FILE: false
SWIFT_STRICT_CONCURRENCY: complete
IPHONEOS_DEPLOYMENT_TARGET: "26.0"
ASSETCATALOG_COMPILER_APPICON_NAME: AppIcon
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME: AccentColor
TARGETED_DEVICE_FAMILY: "1"
DEVELOPMENT_ASSET_PATHS: "\"Workouts/Preview Content\""
scheme:
testTargets:
- WorkoutsTests
# ---- unit tests (domain model / codec, against the iOS app target) --------
WorkoutsTests:
type: bundle.unit-test
platform: iOS
sources:
- WorkoutsTests
dependencies:
- target: Workouts
- package: IndieSync
settings:
base:
PRODUCT_BUNDLE_IDENTIFIER: dev.rzen.indie.WorkoutsTests
GENERATE_INFOPLIST_FILE: true
SWIFT_STRICT_CONCURRENCY: complete
# ---- watchOS app (no iCloud; syncs through the phone via WatchConnectivity) -
Workouts Watch App:
type: application
platform: watchOS
sources:
- path: Shared
- path: Workouts Watch App
excludes:
- "Resources/Info-*.plist"
- "Resources/*.entitlements"
dependencies:
- package: IndieSync
- target: Workouts Watch Widget
embed: true
postBuildScripts: *updateBuildInfo
settings:
base:
PRODUCT_BUNDLE_IDENTIFIER: dev.rzen.indie.Workouts.watchkitapp
INFOPLIST_FILE: "Workouts Watch App/Resources/Info-watchOS.plist"
CODE_SIGN_ENTITLEMENTS: "Workouts Watch App/Resources/Workouts-watchOS.entitlements"
GENERATE_INFOPLIST_FILE: false
SWIFT_STRICT_CONCURRENCY: complete
WATCHOS_DEPLOYMENT_TARGET: "26.0"
ASSETCATALOG_COMPILER_APPICON_NAME: AppIcon
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME: AccentColor
TARGETED_DEVICE_FAMILY: "4"
DEVELOPMENT_ASSET_PATHS: "\"Workouts Watch App/Preview Content\""
scheme:
testTargets:
- Workouts Watch AppTests
# ---- watchOS unit tests (bridge apply/prune seam + watch-side helpers) ------
Workouts Watch AppTests:
type: bundle.unit-test
platform: watchOS
sources:
- Workouts Watch AppTests
dependencies:
- target: Workouts Watch App
- package: IndieSync
settings:
base:
PRODUCT_BUNDLE_IDENTIFIER: dev.rzen.indie.Workouts.watchkitapp.tests
GENERATE_INFOPLIST_FILE: true
SWIFT_STRICT_CONCURRENCY: complete
WATCHOS_DEPLOYMENT_TARGET: "26.0"
# ---- watchOS widget extension (a launcher complication for the watch face) --
Workouts Watch Widget:
type: app-extension
platform: watchOS
sources:
- path: Workouts Watch Widget
excludes:
- "Resources/Info-*.plist"
postBuildScripts: *updateBuildInfo
settings:
base:
PRODUCT_BUNDLE_IDENTIFIER: dev.rzen.indie.Workouts.watchkitapp.widget
INFOPLIST_FILE: "Workouts Watch Widget/Resources/Info-WatchWidget.plist"
GENERATE_INFOPLIST_FILE: false
SWIFT_STRICT_CONCURRENCY: complete
WATCHOS_DEPLOYMENT_TARGET: "26.0"
TARGETED_DEVICE_FAMILY: "4"
# ---- iOS widget extension (the active-workout Live Activity: lock screen + Dynamic Island) --
Workouts Widget:
type: app-extension
platform: iOS
sources:
- path: Workouts Widget
excludes:
- "Resources/Info-*.plist"
postBuildScripts: *updateBuildInfo
settings:
base:
PRODUCT_BUNDLE_IDENTIFIER: dev.rzen.indie.Workouts.widget
INFOPLIST_FILE: "Workouts Widget/Resources/Info-Widget.plist"
GENERATE_INFOPLIST_FILE: false
SWIFT_STRICT_CONCURRENCY: complete
IPHONEOS_DEPLOYMENT_TARGET: "26.0"
TARGETED_DEVICE_FAMILY: "1"
# ---- macOS app (library management MVP: routines/schedules/exercises; no
# workout running/editing). Same bundle id as the iOS app (universal
# purchase); writes only Splits/ and Schedules/ documents. -------------
Workouts Mac:
type: application
platform: macOS
sources:
- path: Shared
excludes:
- "HealthKit/**"
- path: Workouts/Sync
- path: Workouts/Seed
- path: Workouts/ExerciseFigure
- path: Workouts/Views/Exercises/ExerciseCatalog.swift
- path: "Workouts/Views/Exercises/ExerciseDocument+PlanSummary.swift"
- path: Workouts/Views/Exercises/ExerciseInfoContent.swift
- path: Workouts/Views/Routines/RoutineOrdering.swift
- path: Workouts/Views/Library/StarterSeedState.swift
- path: Workouts/Assets.xcassets
- path: Workouts/Resources/ExerciseMotions
buildPhase: resources
- path: Workouts/Resources/StarterSplits
buildPhase: resources
- path: "Workouts Mac"
excludes:
- "Resources/Info-*.plist"
- "Resources/*.entitlements"
- path: CHANGELOG.md
buildPhase: resources
type: file
- path: LICENSE.md
buildPhase: resources
type: file
dependencies:
- package: IndieSync
- package: IndieAbout
postBuildScripts: *updateBuildInfo
settings:
base:
PRODUCT_BUNDLE_IDENTIFIER: dev.rzen.indie.Workouts
PRODUCT_NAME: Workouts
INFOPLIST_FILE: "Workouts Mac/Resources/Info-macOS.plist"
CODE_SIGN_ENTITLEMENTS: "Workouts Mac/Resources/Workouts-macOS.entitlements"
GENERATE_INFOPLIST_FILE: false
SWIFT_STRICT_CONCURRENCY: complete
MACOSX_DEPLOYMENT_TARGET: "26.0"
ASSETCATALOG_COMPILER_APPICON_NAME: AppIcon
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME: AccentColor