Files
workouts/Workouts/Seed/SplitSeeder.swift
T
rzen 3ed7b9272c Seed machine-based starter routine from screenshots
Replace the bodyweight-catalog-derived seed (hardcoded 3x10, weight 0) with an
explicit curated machine-based routine: Upper Body, Core, and Lower Body at 4x10
with starting weights taken from real workout logs. Decoupled from the shared
exercise catalog YAML (still used by the exercise picker).
2026-06-19 15:35:15 -04:00

82 lines
3.5 KiB
Swift

import Foundation
import SwiftData
/// Builds the bundled machine-based starter routine (Upper Body / Core / Lower
/// Body). Written on demand (never auto-seeded) through the SyncEngine an empty
/// cache at launch can't be told apart from an iCloud library that simply hasn't
/// downloaded yet.
enum SplitSeeder {
/// One starter exercise: name plus its default starting weight (lbs).
private struct SeedExercise {
let name: String
let weight: Int
}
/// One starter split: visual theme plus its ordered exercises.
private struct SeedSplit {
let name: String
let color: String
let icon: String
let exercises: [SeedExercise]
}
/// Sets/reps shared by every starter exercise.
private static let defaultSets = 4
private static let defaultReps = 10
/// Starter splits in display order, with sensible machine starting weights.
/// Users adjust weights from the exercise screen.
private static let starterSplits: [SeedSplit] = [
SeedSplit(name: "Upper Body", color: "blue", icon: "figure.strengthtraining.traditional", exercises: [
SeedExercise(name: "Lat Pull Down", weight: 110),
SeedExercise(name: "Tricep Press", weight: 100),
SeedExercise(name: "Chest Press", weight: 40),
SeedExercise(name: "Seated Row", weight: 90),
]),
SeedSplit(name: "Core", color: "orange", icon: "figure.core.training", exercises: [
SeedExercise(name: "Abdominal", weight: 0),
SeedExercise(name: "Rotary", weight: 0),
]),
SeedSplit(name: "Lower Body", color: "green", icon: "figure.run", exercises: [
SeedExercise(name: "Abductor", weight: 140),
SeedExercise(name: "Adductor", weight: 140),
SeedExercise(name: "Leg Press", weight: 160),
SeedExercise(name: "Leg Curl", weight: 70),
]),
]
/// Builds the default split documents (fresh ULIDs each call).
static func defaultSplitDocuments() -> [SplitDocument] {
starterSplits.enumerated().map { order, split in
let exercises = split.exercises.enumerated().map { idx, item in
ExerciseDocument(
id: ULID.make(), name: item.name, order: idx,
sets: defaultSets, reps: defaultReps, weight: item.weight,
loadType: LoadType.weight.rawValue,
durationSeconds: 0, weightLastUpdated: nil, weightReminderWeeks: 2
)
}
return SplitDocument(
schemaVersion: SplitDocument.currentSchema, id: ULID.make(),
name: split.name, color: split.color, systemImage: split.icon, order: order,
createdAt: Date(), updatedAt: Date(), exercises: exercises
)
}
}
/// Writes any starter splits whose name doesn't already exist, appended after
/// existing splits. Idempotent against double-taps / partial prior seeds.
@MainActor
static func seedDefaults(into context: ModelContext, using sync: SyncEngine) async {
let existing = (try? context.fetch(FetchDescriptor<Split>())) ?? []
let existingNames = Set(existing.map(\.name))
let base = existing.count
let fresh = defaultSplitDocuments().filter { !existingNames.contains($0.name) }
for (offset, var doc) in fresh.enumerated() {
doc.order = base + offset
await sync.save(split: doc)
}
}
}