feat(watch): add lap detection via accelerometer and workout sync to phone

This commit is contained in:
2026-08-07 16:35:20 +02:00
parent 00a8220096
commit 48286041c6
8 changed files with 414 additions and 40 deletions
+79 -21
View File
@@ -24,44 +24,83 @@ struct SwimView: View {
workoutManager.currentSetIndex < workout.sets.count {
let currentSet = workout.sets[workoutManager.currentSetIndex]
Text("Set \(workoutManager.currentSetIndex + 1)/\(workout.sets.count)")
.font(.caption2)
.foregroundColor(.secondary)
// Set/Rep info
HStack {
Text("Set \(workoutManager.currentSetIndex + 1)/\(workout.sets.count)")
.font(.caption2)
.foregroundColor(.secondary)
Text("·")
.foregroundColor(.secondary)
Text("Rep \(workoutManager.currentRep + 1)/\(currentSet.repCount)")
.font(.caption2)
.foregroundColor(.secondary)
}
// Stroke name
Text(currentSet.stroke)
.font(.headline)
.foregroundColor(.cyan)
Text("Rep \(workoutManager.currentRep + 1)/\(currentSet.repCount)")
.font(.subheadline)
Text("\(currentSet.distancePerRep)m")
// Current set distance
Text("\(Int(workoutManager.currentSetDistanceSwum))/\(Int(workoutManager.currentSetTotalDistance))m")
.font(.system(size: 36, weight: .bold, design: .rounded))
.foregroundColor(.cyan)
}
Text(formatTime(workoutManager.elapsedSeconds))
.font(.system(.title, design: .monospaced))
// Total distance
HStack(spacing: 4) {
Image(systemName: "figure.pool.swim")
.font(.caption)
.foregroundColor(.cyan)
Text("\(Int(workoutManager.totalDistanceSwum))m total")
.font(.caption)
.foregroundColor(.secondary)
}
if workoutManager.heartRate > 0 {
// Elapsed time
Text(formatTime(workoutManager.elapsedSeconds))
.font(.system(.title3, design: .monospaced))
// Calories + Heart rate
HStack(spacing: 12) {
HStack(spacing: 4) {
Image(systemName: "heart.fill")
.foregroundColor(.red)
Image(systemName: "flame.fill")
.foregroundColor(.orange)
.font(.caption)
Text("\(Int(workoutManager.heartRate))")
Text("\(Int(workoutManager.estimatedCalories))")
.font(.caption)
}
if workoutManager.heartRate > 0 {
HStack(spacing: 4) {
Image(systemName: "heart.fill")
.foregroundColor(.red)
.font(.caption)
Text("\(Int(workoutManager.heartRate))")
.font(.caption)
}
}
}
Button {
workoutManager.completeLap()
} label: {
Text("Complete Lap")
.font(.headline)
.frame(maxWidth: .infinity)
// Lap detection indicator
if workoutManager.lapDetected {
Text("Lap detected!")
.font(.caption)
.foregroundColor(.green)
.transition(.opacity)
}
HStack(spacing: 12) {
Button {
workoutManager.completeLap()
} label: {
Text("Complete Lap")
.font(.headline)
.frame(maxWidth: .infinity)
}
.buttonStyle(.borderedProminent)
.tint(.cyan)
}
.buttonStyle(.borderedProminent)
.tint(.cyan)
Button("End") {
workoutManager.endWorkout()
@@ -98,6 +137,25 @@ struct RestView: View {
.font(.caption)
.foregroundColor(.secondary)
}
// Show stats during rest too
HStack(spacing: 12) {
HStack(spacing: 4) {
Image(systemName: "figure.pool.swim")
.font(.caption)
.foregroundColor(.cyan)
Text("\(Int(workoutManager.totalDistanceSwum))m")
.font(.caption)
}
HStack(spacing: 4) {
Image(systemName: "flame.fill")
.font(.caption)
.foregroundColor(.orange)
Text("\(Int(workoutManager.estimatedCalories))kcal")
.font(.caption)
}
}
.foregroundColor(.secondary)
}
}