From 3ed7b9272cbd3f703b0321ad8379809a785f3111 Mon Sep 17 00:00:00 2001 From: rzen Date: Fri, 19 Jun 2026 15:35:15 -0400 Subject: [PATCH] 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). --- CHANGELOG.md | 4 +- Workouts/Seed/SplitSeeder.swift | 74 +++++++++++++++++++++------------ 2 files changed, 50 insertions(+), 28 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e16157a..888b3dc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,8 +14,8 @@ All notable changes to this project are documented here. ULIDs (the phone is the sole writer of iCloud Drive). - Migrated the project to XcodeGen; iOS 26 / watchOS 26, Swift 6 strict concurrency. -- Splits ship as on-demand starter data generated from the bundled exercise - catalogs. +- Splits ship as an on-demand machine-based starter routine (Upper Body, Core, + Lower Body) at 4×10 with sensible starting weights. - Stored exercise/log durations as integer seconds (was a `Date` epoch hack). - Fixed: workout marked complete on creation, an undismissable delete dialog, toolbar buttons hidden by nested navigation stacks, and a placeholder diff --git a/Workouts/Seed/SplitSeeder.swift b/Workouts/Seed/SplitSeeder.swift index 83e0ae8..952eb23 100644 --- a/Workouts/Seed/SplitSeeder.swift +++ b/Workouts/Seed/SplitSeeder.swift @@ -1,45 +1,67 @@ import Foundation import SwiftData -/// Builds starter Splits from the bundled YAML exercise catalog, grouped by the -/// catalog's `split` label (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. +/// 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 { - /// Visual theme per starter split group, in display order. - private static let groups: [(name: String, color: String, icon: String)] = [ - ("Upper Body", "blue", "figure.strengthtraining.traditional"), - ("Core", "orange", "figure.core.training"), - ("Lower Body", "green", "figure.run"), - ] + /// One starter exercise: name plus its default starting weight (lbs). + private struct SeedExercise { + let name: String + let weight: Int + } - /// Default split source catalog (most universally applicable). - private static let sourceFile = "bodyweight-starter.exercises.yaml" + /// 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] { - let lists = ExerciseListLoader.loadExerciseLists() - guard let catalog = lists[sourceFile] else { return [] } - - var docs: [SplitDocument] = [] - for (order, group) in groups.enumerated() { - let items = catalog.exercises.filter { $0.split == group.name } - guard !items.isEmpty else { continue } - - let exercises = items.enumerated().map { idx, item in + 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: 3, reps: 10, weight: 0, loadType: LoadType.weight.rawValue, + sets: defaultSets, reps: defaultReps, weight: item.weight, + loadType: LoadType.weight.rawValue, durationSeconds: 0, weightLastUpdated: nil, weightReminderWeeks: 2 ) } - docs.append(SplitDocument( + return SplitDocument( schemaVersion: SplitDocument.currentSchema, id: ULID.make(), - name: group.name, color: group.color, systemImage: group.icon, order: order, + name: split.name, color: split.color, systemImage: split.icon, order: order, createdAt: Date(), updatedAt: Date(), exercises: exercises - )) + ) } - return docs } /// Writes any starter splits whose name doesn't already exist, appended after