Files
workouts/Shared/Screenshots/ScreenshotSeed.swift
T
rzen 2c1e4759ae 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
2026-07-06 16:29:44 -04:00

107 lines
5.3 KiB
Swift

#if DEBUG
import IndieSync
import Foundation
import SwiftData
/// DEBUG-only sample data + launch-arg plumbing for App Store screenshot capture.
/// Activated by launching with `--screenshot`; `--screen <name>` picks which screen
/// the app renders (see each target's screenshot root). Never compiled into release.
enum ScreenshotSeed {
static var isActive: Bool { CommandLine.arguments.contains("--screenshot") }
static func screen(default def: String) -> String {
let args = CommandLine.arguments
if let i = args.firstIndex(of: "--screen"), i + 1 < args.count { return args[i + 1] }
return def
}
/// Insert a believable in-progress workout, a few finished sessions (so charts have
/// a trend), and the starter splits. Returns the in-progress workout to display.
@MainActor
@discardableResult
static func populate(_ context: ModelContext) -> Workout? {
// Idempotent: clear any prior seed so repeated launches don't stack duplicates
// (cascade deletes take the child exercises/logs with them).
try? context.delete(model: Workout.self)
try? context.delete(model: Split.self)
let cal = Calendar(identifier: .gregorian)
let today = Date()
func daysAgo(_ n: Int) -> Date { cal.date(byAdding: .day, value: -n, to: today) ?? today }
// ---- Splits (with exercises) ------------------------------------------
// Names match the bundled Exercise Library rigs so every screen shows its
// animated figure (an unknown name would leave the figure slot empty).
let splits: [(String, String, String, [(String, Int, Int, Int)])] = [
("Upper Body", "blue", "figure.strengthtraining.traditional", [
("Lat Pull Down", 4, 10, 110), ("Seated Row", 4, 10, 90),
("Shoulder Press", 4, 10, 40), ("Arm Curl", 3, 12, 40),
]),
("Lower Body", "green", "figure.run", [
("Leg Press", 4, 10, 140), ("Leg Curl", 4, 10, 40),
("Leg Extension", 4, 10, 80), ("Calfs", 4, 15, 100),
]),
("Core", "orange", "figure.core.training", [
("Plank", 3, 0, 0), ("Abdominal", 4, 10, 40),
("Rotary", 4, 10, 40),
]),
]
for (sIndex, s) in splits.enumerated() {
let split = Split(id: ULID.make(), name: s.0, color: s.1, systemImage: s.2,
order: sIndex, createdAt: today, updatedAt: today, jsonRelativePath: "")
context.insert(split)
for (eIndex, e) in s.3.enumerated() {
let isDuration = e.0 == "Plank"
let ex = Exercise(id: ULID.make(), name: e.0, order: eIndex, sets: e.1, reps: e.2,
weight: e.3, loadType: (isDuration ? LoadType.duration : .weight).rawValue,
durationTotalSeconds: isDuration ? 45 : 0)
ex.split = split
context.insert(ex)
}
}
let upper = splits[0]
// ---- Past finished sessions (Lat Pull Down trend for the chart) --------
let liftTrend = [95, 100, 105, 110]
for (i, w) in liftTrend.enumerated() {
let date = daysAgo((liftTrend.count - i) * 4)
let workout = Workout(id: ULID.make(), splitID: nil, splitName: upper.0, start: date,
end: date.addingTimeInterval(2400), statusRaw: WorkoutStatus.completed.rawValue,
createdAt: date, updatedAt: date, jsonRelativePath: "")
context.insert(workout)
for (eIndex, e) in upper.3.enumerated() {
let weight = e.0 == "Lat Pull Down" ? w : e.3
let log = WorkoutLog(id: ULID.make(), exerciseName: e.0, order: eIndex, sets: e.1,
reps: e.2, weight: weight, loadType: LoadType.weight.rawValue,
durationTotalSeconds: 0, currentStateIndex: e.1,
statusRaw: WorkoutStatus.completed.rawValue, notes: nil, date: date)
log.workout = workout
context.insert(log)
}
}
// ---- The current in-progress session -----------------------------------
let workout = Workout(id: ULID.make(), splitID: nil, splitName: upper.0, start: today,
end: nil, statusRaw: WorkoutStatus.inProgress.rawValue,
createdAt: today, updatedAt: today, jsonRelativePath: "")
context.insert(workout)
// Lat Pull Down done, Seated Row partway, the rest to go.
let progress = [(4, WorkoutStatus.completed), (2, .inProgress), (0, .notStarted), (0, .notStarted)]
for (eIndex, e) in upper.3.enumerated() {
let (idx, status) = progress[eIndex]
let log = WorkoutLog(id: ULID.make(), exerciseName: e.0, order: eIndex, sets: e.1, reps: e.2,
weight: e.0 == "Lat Pull Down" ? 110 : e.3, loadType: LoadType.weight.rawValue,
durationTotalSeconds: 0, currentStateIndex: idx,
statusRaw: status.rawValue, notes: nil, date: today)
log.workout = workout
context.insert(log)
}
try? context.save()
return workout
}
}
#endif