Files
workoutsplus/WorkoutsPlus/Features/Workout/Workout.swift
2024-10-21 15:03:47 +02:00

146 lines
3.7 KiB
Swift

//
// 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 start() -> WorkoutSession {
return WorkoutSession(start: self)
}
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]
}()
}