Files
workouts/Shared/Model/Enums.swift
T
rzen e12fe31152 Add Warm-Up and Stretching activity types
Adds WorkoutActivityType.warmUp (HealthKit .preparationAndRecovery) and
.stretching (.flexibility), and retags the six starter splits that were all
mislabeled as Functional Strength:

- Warm-Up:    Upper Body Warm-Up, Lower Body Warm-Up, Morning Wake-Up
- Stretching: Morning Mobility, Full Body Stretch, Evening Stretch

The split editor's activity picker surfaces them automatically (CaseIterable).
Older app versions decode the new raw values as the default type — additive and
not schema-gated, so no quarantine.
2026-07-09 15:45:31 -04:00

122 lines
3.8 KiB
Swift

import Foundation
/// Completion state of a workout or an individual exercise log. Persisted as its
/// raw string in both the JSON documents and the SwiftData cache.
enum WorkoutStatus: String, CaseIterable, Codable, Sendable {
case notStarted
case inProgress
case completed
case skipped
var displayName: String {
switch self {
case .notStarted: "Not Started"
case .inProgress: "In Progress"
case .completed: "Completed"
case .skipped: "Skipped"
}
}
var name: String { displayName }
}
/// How an exercise's effort is measured. Persisted as its raw `Int` value.
enum LoadType: Int, CaseIterable, Codable, Sendable {
case none = 0
case weight = 1
case duration = 2
var name: String {
switch self {
case .none: "None"
case .weight: "Weight"
case .duration: "Duration"
}
}
}
/// The kind of training a split represents — used to tag the watch's Apple Health
/// workout so it lands in the right category (and credits the rings correctly).
/// Persisted as its raw `Int` on `SplitDocument`; the `HKWorkoutActivityType`
/// mapping lives in `Shared/HealthKit/HealthKitMapping.swift` to keep this enum
/// HealthKit-free.
enum WorkoutActivityType: Int, CaseIterable, Codable, Sendable {
case traditionalStrength = 0 // default
case functionalStrength = 1
case hiit = 2
case coreTraining = 3
case cardio = 4
case cycling = 5
case warmUp = 6
case stretching = 7
var displayName: String {
switch self {
case .traditionalStrength: "Strength Training"
case .functionalStrength: "Functional Strength"
case .hiit: "HIIT"
case .coreTraining: "Core Training"
case .cardio: "Cardio"
case .cycling: "Cycling"
case .warmUp: "Warm-Up"
case .stretching: "Stretching"
}
}
var systemImage: String {
switch self {
case .traditionalStrength: "dumbbell.fill"
case .functionalStrength: "figure.strengthtraining.functional"
case .hiit: "figure.highintensity.intervaltraining"
case .coreTraining: "figure.core.training"
case .cardio: "figure.run"
case .cycling: "figure.outdoor.cycle"
case .warmUp: "figure.arms.open"
case .stretching: "figure.flexibility"
}
}
}
/// Where a workout's health metrics came from: real watch sensors, or — for records
/// written by app versions up to 2.x — a phone-side MET estimate made when no watch
/// session ran. The phone no longer writes estimates (recording is watch-only), but
/// `phoneEstimate` is retained so legacy documents on disk still decode. Persisted in
/// `WorkoutMetrics`.
enum MetricSource: String, Codable, Sendable {
case watch
case phoneEstimate
}
/// Display unit for weights. The stored weight values are unit-less numbers and
/// are never rewritten when this changes — switching only relabels what's shown.
enum WeightUnit: String, CaseIterable, Codable, Sendable {
case lb
case kg
var displayName: String {
switch self {
case .lb: "Pounds (lb)"
case .kg: "Kilograms (kg)"
}
}
var abbreviation: String {
switch self {
case .lb: "lb"
case .kg: "kg"
}
}
/// Render a stored weight value with the current unit's label, trimming a
/// trailing ".0" so whole values stay clean: "45 lb", "42.5 kg".
func format(_ value: Double) -> String {
let text = value.truncatingRemainder(dividingBy: 1) == 0
? String(Int(value))
: String(value)
return "\(text) \(abbreviation)"
}
/// Render a stored weight value with the current unit's label, e.g. "135 lb".
func format(_ value: Int) -> String { "\(value) \(abbreviation)" }
}