implement laps, reset
This commit is contained in:
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user