PersistableTimer is a Swift library that provides persistent timers and stopwatches with seamless state restoration — even across app restarts. It supports both countdown timers and stopwatches, with flexible data sources such as UserDefaults (for production) and in-memory storage (for testing or previews).
- Persistent State: Restore timer state automatically after app termination or restart.
- Dual Modes: Choose between a running stopwatch and a countdown timer.
- Real-time Updates: Subscribe to continuous timer updates via an asynchronous stream.
- Dynamic Time Adjustment: Add extra time to a countdown or extra elapsed time to a stopwatch.
- SwiftUI Integration: Easily display timer states using extensions from
PersistableTimerText
.
See the Example App for a complete SwiftUI implementation.
Add the package dependency in your Package.swift
:
dependencies: [
.package(url: "https://github.com/Ryu0118/swift-persistable-timer.git", from: "0.7.0")
],
Then add the desired products (PersistableTimer
, PersistableTimerCore
, or PersistableTimerText
) to your target dependencies.
Instantiate PersistableTimer
with your preferred data source and configuration:
import PersistableTimer
// For testing or previews:
let timer = PersistableTimer(dataSourceType: .inMemory)
// For production (using UserDefaults):
let timer = PersistableTimer(dataSourceType: .userDefaults(.standard))
// With a custom update interval:
let timer = PersistableTimer(dataSourceType: .userDefaults(.standard), updateInterval: 0.5)
Start a stopwatch or a countdown timer. You can also force-start a new timer even if one is already running:
// Start a stopwatch
try await timer.start(type: .stopwatch)
// Start a countdown timer with a duration of 100 seconds
try await timer.start(type: .timer(duration: 100))
// Force start a new timer even if one is already active:
try await timer.start(type: .timer(duration: 100), forceStart: true)
Control the timer state as needed:
// Pause the timer
try await timer.pause()
// Resume a paused timer
try await timer.resume()
// Finish the timer (optionally reset the elapsed time)
try await timer.finish(isResetTime: false)
Adjust the timer on the fly:
// For countdown timers: add extra time to the remaining duration.
try await timer.addRemainingTime(5) // Adds 5 seconds
// For stopwatches: add extra elapsed time (i.e., effectively moving the start date earlier).
try await timer.addElapsedTime(5) // Adds 5 seconds to the elapsed time
Restore the timer's previous state after an app restart:
try timer.restore()
Subscribe to the asynchronous time stream to update your UI in real time:
for await timeState in timer.timeStream {
// Update your UI with the current timer state.
print("Elapsed time: \(timeState.elapsedTime)")
}
Display the timer state easily in your SwiftUI views using the provided Text initializer:
import SwiftUI
import PersistableTimerText
struct TimerView: View {
@State private var timerState: TimerState?
var body: some View {
Text(timerState: timerState)
.font(.title)
.onAppear {
// Update `timerState` with your PersistableTimer's current state.
}
}
}
This project is licensed under the MIT License. See the LICENSE file for details.