#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 ` 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 routines. 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: Routine.self) try? context.delete(model: Schedule.self) let cal = Calendar(identifier: .gregorian) let today = Date() func daysAgo(_ n: Int) -> Date { cal.date(byAdding: .day, value: -n, to: today) ?? today } // ---- Routines (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 routines: [(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), ]), ] var created: [Routine] = [] for (sIndex, s) in routines.enumerated() { let routine = Routine(id: ULID.make(), name: s.0, color: s.1, systemImage: s.2, order: sIndex, createdAt: today, updatedAt: today, jsonRelativePath: "") context.insert(routine) created.append(routine) 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: Double(e.3), loadType: (isDuration ? LoadType.duration : .weight).rawValue, durationTotalSeconds: isDuration ? 45 : 0) ex.routine = routine context.insert(ex) } } // ---- Schedules (the Today board's rows, spanning two goal groups) ------- let schedulePlans: [(routine: Routine, goal: GoalKind, recurrence: ScheduleRecurrence, weekdays: [Int]?)] = [ (created[0], .strength, .fixedDays, [2, 5]), // Upper Body · Mon & Thu (created[1], .strength, .fixedDays, [3, 7]), // Lower Body · Tue & Sat (created[2], .mobility, .daily, nil), // Core · daily ] // Schedules predate the history below, so the Progress tab's adherence // tracks cover the seeded weeks instead of starting "today". let scheduledSince = daysAgo(70) for (i, plan) in schedulePlans.enumerated() { context.insert(Schedule( id: ULID.make(), routineID: plan.routine.id, routineName: plan.routine.name, goalRaw: plan.goal.rawValue, recurrenceRaw: plan.recurrence.rawValue, weekdays: plan.weekdays, order: i, createdAt: scheduledSince, updatedAt: scheduledSince, jsonRelativePath: "")) } let upper = routines[0] let upperID = created[0].id // ---- Past finished sessions --------------------------------------------- // Ten weeks of believable history across all three routines so Progress has // living goal tracks, streaks, trends, and history: Upper Body lands on its // Mon & Thu slots (one missed), Lower Body twice a week (one missed), Core // most mornings — unbroken in the last four weeks, so the Mobility streak is // alive. Upper's Lat Pull Down ramps toward 110 for the progression chart. // All deterministic — patterns, not randomness. func addCompleted(routine: Routine, spec: [(String, Int, Int, Int)], on day: Date, hour: Int, lengthMinutes: Int, latPullDown: Double?) { let start = cal.date(bySettingHour: hour, minute: 15, second: 0, of: day) ?? day let end = start.addingTimeInterval(Double(lengthMinutes) * 60) let workout = Workout(id: ULID.make(), routineID: routine.id, routineName: routine.name, start: start, end: end, statusRaw: WorkoutStatus.completed.rawValue, createdAt: start, updatedAt: end, jsonRelativePath: "") context.insert(workout) for (eIndex, e) in spec.enumerated() { let isDuration = e.0 == "Plank" let weight = e.0 == "Lat Pull Down" ? (latPullDown ?? Double(e.3)) : Double(e.3) let log = WorkoutLog(id: ULID.make(), exerciseName: e.0, order: eIndex, sets: e.1, reps: e.2, weight: isDuration ? 0 : weight, loadType: (isDuration ? LoadType.duration : .weight).rawValue, durationTotalSeconds: isDuration ? 45 : 0, currentStateIndex: e.1, statusRaw: WorkoutStatus.completed.rawValue, notes: nil, date: start, setEntries: isDuration ? nil : actualEntries(sets: e.1, reps: e.2, weight: weight, at: start)) log.workout = workout context.insert(log) } } var upperSlot = 0, lowerSlot = 0 for d in stride(from: 69, through: 1, by: -1) { let day = daysAgo(d) let weekday = cal.component(.weekday, from: day) // Upper Body — Mon & Thu; the 5th slot is the believable miss. if weekday == 2 || weekday == 5 { upperSlot += 1 if upperSlot != 5 { let lift = min(110, 87.5 + 2.5 * Double((upperSlot + 1) / 2)) addCompleted(routine: created[0], spec: routines[0].3, on: day, hour: 17, lengthMinutes: 40, latPullDown: lift) } } // Lower Body — Tue & Sat, its 2×-per-week rhythm; the 4th slot missed. if weekday == 3 || weekday == 7 { lowerSlot += 1 if lowerSlot != 4 { addCompleted(routine: created[1], spec: routines[1].3, on: day, hour: 18, lengthMinutes: 35, latPullDown: nil) } } // Core — daily mornings; Sundays skipped beyond the last four weeks. if d <= 28 || weekday != 1 { addCompleted(routine: created[2], spec: routines[2].3, on: day, hour: 6, lengthMinutes: 15, latPullDown: nil) } } // ---- The current in-progress session ----------------------------------- // Carries the routine's id so the Today board's schedule row shows it as // today's in-progress run. let workout = Workout(id: ULID.make(), routineID: upperID, routineName: 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 weight = e.0 == "Lat Pull Down" ? 110 : Double(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: idx, statusRaw: status.rawValue, notes: nil, date: today, setEntries: idx > 0 ? actualEntries(sets: idx, reps: e.2, weight: weight, at: today) : nil) log.workout = workout context.insert(log) } try? context.save() return workout } /// Plausible per-set actuals for a weighted log: plan values, with the last /// set a couple of reps short so screenshots show real-looking variation. private static func actualEntries(sets: Int, reps: Int, weight: Double, at date: Date) -> [SetEntry] { (0..