Files
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

36 lines
1.2 KiB
Swift

import Foundation
import Observation
import SwiftData
/// Composition root for the Mac app. Owns the SwiftData cache container and the
/// iCloud sync engine only — no watch bridge, HealthKit, ActivityKit, backup, or
/// reminders/speech. The Mac's MVP is browsing and managing the library
/// (routines/schedules/exercises); it never runs a workout. Injected into the
/// view tree via `.environment(...)`.
@Observable
@MainActor
final class MacAppServices {
let container: ModelContainer
let syncEngine: SyncEngine
private var bootstrapTask: Task<Void, Never>?
init() {
let container = WorkoutsModelContainer.make()
self.container = container
self.syncEngine = SyncEngine(container: container)
}
/// Launch step: resolve iCloud and reconcile the cache. Idempotent — repeated
/// callers await the same one-shot task.
func bootstrap() async {
if let bootstrapTask { await bootstrapTask.value; return }
let task = Task { @MainActor [weak self] in
guard let self else { return }
await self.syncEngine.connect()
}
bootstrapTask = task
await task.value
}
}