implement laps, reset

This commit is contained in:
2026-01-28 23:09:21 +01:00
parent 750a096999
commit a607e33ce9
3 changed files with 159 additions and 18 deletions

View File

@@ -9,17 +9,26 @@ import Foundation
import Combine
class Stopwatch: ObservableObject, Identifiable, Codable {
struct Lap: Identifiable, Codable {
let id: UUID
let number: Int
let startTime: Date
let endTime: Date
let duration: TimeInterval
}
let id: UUID
@Published var name: String
@Published var elapsedTime: TimeInterval = 0
@Published var isRunning: Bool = false
@Published var laps: [Lap] = []
private var timer: AnyCancellable?
private var startTime: Date?
private var accumulatedTime: TimeInterval = 0
enum CodingKeys: String, CodingKey {
case id, name, elapsedTime, isRunning, startTime, accumulatedTime
case id, name, elapsedTime, isRunning, startTime, accumulatedTime, laps
}
init(name: String) {
@@ -35,6 +44,7 @@ class Stopwatch: ObservableObject, Identifiable, Codable {
isRunning = try container.decode(Bool.self, forKey: .isRunning)
startTime = try container.decodeIfPresent(Date.self, forKey: .startTime)
accumulatedTime = try container.decode(TimeInterval.self, forKey: .accumulatedTime)
laps = try container.decodeIfPresent([Lap].self, forKey: .laps) ?? []
if isRunning {
// Restart the timer if it was running.
@@ -54,6 +64,7 @@ class Stopwatch: ObservableObject, Identifiable, Codable {
try container.encode(isRunning, forKey: .isRunning)
try container.encode(startTime, forKey: .startTime)
try container.encode(accumulatedTime, forKey: .accumulatedTime)
try container.encode(laps, forKey: .laps)
}
deinit {
@@ -82,8 +93,20 @@ class Stopwatch: ObservableObject, Identifiable, Codable {
timer?.cancel()
timer = nil
let now = Date()
if let startTime = startTime {
accumulatedTime += Date().timeIntervalSince(startTime)
let sessionDuration = now.timeIntervalSince(startTime)
accumulatedTime += sessionDuration
// Record Lap
let newLap = Lap(
id: UUID(),
number: laps.count + 1,
startTime: startTime,
endTime: now,
duration: sessionDuration
)
laps.append(newLap)
}
isRunning = false
@@ -94,9 +117,17 @@ class Stopwatch: ObservableObject, Identifiable, Codable {
}
func reset() {
pause()
// Stop timer without recording a lap? Or should reset clear laps?
// Usually reset clears everything.
if isRunning {
timer?.cancel()
timer = nil
isRunning = false
startTime = nil
}
accumulatedTime = 0
elapsedTime = 0
laps.removeAll()
}
private func tick() {
@@ -104,28 +135,28 @@ class Stopwatch: ObservableObject, Identifiable, Codable {
elapsedTime = accumulatedTime + Date().timeIntervalSince(startTime)
}
func formattedTime(format: TimeFormat = .cents) -> String {
let hours = Int(elapsedTime) / 3600
let minutes = Int(elapsedTime) / 60 % 60
let seconds = Int(elapsedTime) % 60
static func format(interval: TimeInterval, format: TimeFormat) -> String {
let hours = Int(interval) / 3600
let minutes = Int(interval) / 60 % 60
let seconds = Int(interval) % 60
switch format {
case .millis:
let millis = Int((elapsedTime.truncatingRemainder(dividingBy: 1)) * 1000)
if hours > 0 {
return String(format: "%02i:%02i:%02i.%03i", hours, minutes, seconds, millis)
} else {
return String(format: "%02i:%02i.%03i", minutes, seconds, millis)
}
let millis = Int((interval.truncatingRemainder(dividingBy: 1)) * 1000)
if hours > 0 {
return String(format: "%02i:%02i:%02i.%03i", hours, minutes, seconds, millis)
} else {
return String(format: "%02i:%02i.%03i", minutes, seconds, millis)
}
case .cents:
let cents = Int((elapsedTime.truncatingRemainder(dividingBy: 1)) * 100)
let cents = Int((interval.truncatingRemainder(dividingBy: 1)) * 100)
if hours > 0 {
return String(format: "%02i:%02i:%02i.%02i", hours, minutes, seconds, cents)
} else {
return String(format: "%02i:%02i.%02i", minutes, seconds, cents)
}
case .tenths:
let tenths = Int((elapsedTime.truncatingRemainder(dividingBy: 1)) * 10)
let tenths = Int((interval.truncatingRemainder(dividingBy: 1)) * 10)
if hours > 0 {
return String(format: "%02i:%02i:%02i.%01i", hours, minutes, seconds, tenths)
} else {
@@ -140,8 +171,21 @@ class Stopwatch: ObservableObject, Identifiable, Codable {
}
}
func formattedTime(format: TimeFormat = .cents) -> String {
return Stopwatch.format(interval: elapsedTime, format: format)
}
// Legacy support to avoid breaking existing code immediately
var formattedTime: String {
formattedTime(format: AppSettings.shared.timeFormat)
}
var currentRunDuration: TimeInterval? {
guard let startTime = startTime else { return nil }
return Date().timeIntervalSince(startTime)
}
var currentRunStartTime: Date? {
return startTime
}
}