Add machine comfort settings and tighten the document schemas

Machine-based exercises now carry ordered name/value comfort settings
(seat height, back-rest position, ...) on both ExerciseDocument and, as a
plan-time snapshot, WorkoutLogDocument — the optional array doubles as the
machine flag. Editable from the exercise editor's new Machine section and
from the workout row's settings sheet, whose mid-workout edits write back
to the originating split (workout saved first, so seed clone-on-edit
repointing can't clobber the log edit).

Schema tightening rides the same rev: splits bump to v2 (weight-reminder
fields and the unused exercise category removed), workouts to v3 (the
derived `completed` flag removed; status is the single source). Starter
seeds regenerated at v2 with unchanged ULIDs; SwiftData cache schema
bumped to rebuild. SCHEMA.md documents the shapes.

Claude-Session: https://claude.ai/code/session_01LEoff8bXGBS83tK1c55Mf7
This commit is contained in:
2026-07-06 16:29:44 -04:00
parent fce8fa4c17
commit 2c1e4759ae
33 changed files with 1132 additions and 586 deletions
+19 -17
View File
@@ -62,15 +62,16 @@ final class Exercise {
var weight: Int = 0
var loadType: Int = LoadType.weight.rawValue
var durationTotalSeconds: Int = 0
var weightLastUpdated: Date?
var weightReminderTimeIntervalWeeks: Int = 2
var categoryRaw: Int = ExerciseCategory.main.rawValue
// Machine comfort settings stored directly as an optional Codable array
// (SwiftData persists the `[MachineSetting]` composite), mirroring how
// `Workout` stores `hrZoneSeconds` as a plain `[Double]?`. `nil` not a
// machine exercise; empty a machine exercise with nothing recorded yet.
var machineSettings: [MachineSetting]?
var split: Split?
init(id: String, name: String, order: Int, sets: Int, reps: Int, weight: Int,
loadType: Int, durationTotalSeconds: Int, weightLastUpdated: Date?,
weightReminderTimeIntervalWeeks: Int, categoryRaw: Int = ExerciseCategory.main.rawValue) {
loadType: Int, durationTotalSeconds: Int, machineSettings: [MachineSetting]? = nil) {
self.id = id
self.name = name
self.order = order
@@ -79,9 +80,7 @@ final class Exercise {
self.weight = weight
self.loadType = loadType
self.durationTotalSeconds = durationTotalSeconds
self.weightLastUpdated = weightLastUpdated
self.weightReminderTimeIntervalWeeks = weightReminderTimeIntervalWeeks
self.categoryRaw = categoryRaw
self.machineSettings = machineSettings
}
var loadTypeEnum: LoadType {
@@ -89,11 +88,6 @@ final class Exercise {
set { loadType = newValue.rawValue }
}
var categoryEnum: ExerciseCategory {
get { ExerciseCategory(rawValue: categoryRaw) ?? .main }
set { categoryRaw = newValue.rawValue }
}
/// One-line plan summary for list rows duration exercises show their hold time
/// ("3 × 45 sec"), unloaded ones drop the weight ("3 × 12 reps"), weighted ones
/// keep the full "3 × 10 × 40 lb".
@@ -233,16 +227,22 @@ final class WorkoutLog {
var loadType: Int = LoadType.weight.rawValue
var durationTotalSeconds: Int = 0
var currentStateIndex: Int = 0
var completed: Bool = false
var statusRaw: String = WorkoutStatus.notStarted.rawValue
var notes: String?
var date: Date = Date()
// Snapshot of the exercise's machine comfort settings at plan time stored
// directly as an optional Codable array, same pattern as `Exercise`.
var machineSettings: [MachineSetting]?
var startedAt: Date?
var completedAt: Date?
var workout: Workout?
init(id: String, exerciseName: String, order: Int, sets: Int, reps: Int, weight: Int,
loadType: Int, durationTotalSeconds: Int, currentStateIndex: Int, completed: Bool,
statusRaw: String, notes: String?, date: Date) {
loadType: Int, durationTotalSeconds: Int, currentStateIndex: Int,
statusRaw: String, notes: String?, date: Date,
machineSettings: [MachineSetting]? = nil,
startedAt: Date? = nil, completedAt: Date? = nil) {
self.id = id
self.exerciseName = exerciseName
self.order = order
@@ -252,10 +252,12 @@ final class WorkoutLog {
self.loadType = loadType
self.durationTotalSeconds = durationTotalSeconds
self.currentStateIndex = currentStateIndex
self.completed = completed
self.statusRaw = statusRaw
self.notes = notes
self.date = date
self.machineSettings = machineSettings
self.startedAt = startedAt
self.completedAt = completedAt
}
var status: WorkoutStatus {