// // Workout.swift // WorkoutsPlus // // Created by Felix Förtsch on 10.08.24. // import SwiftUI import SwiftData @Model final class Workout: Nameable, Hashable { static var systemImage = "figure.run.square.stack" var id = UUID() var creationDate = Date.now // The name of my workout is: Recommended Routine, My Marathon Workout @Attribute(.unique) var name: String var defaultRestTime: TimeInterval = 60 var useDefaultRestTime: Bool = false // Icon var workoutIconSystemName = "figure.run" var workoutIconColorName = ColorName.black // TODO: Expected Duration (learn from past workouts with ML and predict workout duration from that @Relationship(deleteRule: .cascade) private var workoutItems: [WorkoutItem] = [] func getWorkoutItems() -> [WorkoutItem] { return workoutItems.sorted { $0.position < $1.position } } init(name: String) { self.name = name } func add(workoutItem: WorkoutItem) { self.workoutItems.append(workoutItem) updateWorkoutItemsPositions() } func add(workoutItems: [WorkoutItem]) { for workoutItem in workoutItems { self.workoutItems.append(workoutItem) } updateWorkoutItemsPositions() } func moveWorkoutItem(from source: IndexSet, to destination: Int) { workoutItems.move(fromOffsets: source, toOffset: destination) updateWorkoutItemsPositions() } private func updateWorkoutItemsPositions() { for (index, exercise) in workoutItems.enumerated() { exercise.position = index } } func isSelected(workout: Workout) -> Bool { self.id == workout.id } } extension Workout { // This enum maps the system colors to enable storing them with the SwiftData model data as String. // See Workout.workoutIconColorName for a usage example. // TODO: Use a Macro to reduce this horrible contraption to something maintainable. enum ColorName: String, Codable { case black case blue case cyan case gray case green case indigo case mint case orange case pink case purple case red case white case yellow var color: Color { switch self { case .black: return .black case .blue: return .blue case .cyan: return .cyan case .gray: return .gray case .green: return .green case .indigo: return .indigo case .mint: return .mint case .orange: return .orange case .pink: return .pink case .purple: return .purple case .red: return .red case .white: return .white case .yellow: return .yellow } } static func fromColor(_ color: Color) -> ColorName? { switch color { case .black: return .black case .blue: return .blue case .cyan: return .cyan case .gray: return .gray case .green: return .green case .indigo: return .indigo case .mint: return .mint case .orange: return .orange case .pink: return .pink case .purple: return .purple case .red: return .red case .white: return .white case .yellow: return .yellow default: return nil } } } } extension Workout { private convenience init(name: String, workoutItems: [WorkoutItem]) { self.init(name: name) self.workoutItems = workoutItems } static let sampleData: [Workout] = { var rr = Workout(name: "Recommended Routine") for workoutItem in WorkoutItem.sampleDataRecommendedRoutine { rr.add(workoutItem: workoutItem) } var rings = Workout(name: "Fully Body Rings") for workoutItem in WorkoutItem.sampleDataRings { rings.add(workoutItem: workoutItem) } return [rr, rings] }() }