enter image description here
On Xcode 15.0.1, I’m trying to use SwiftData to create a StopwatchItem
in my stopwatch app, which has a timer
property. I get the errors:
-
Instance method ‘setValue(forKey:to:)’ requires that ‘Timer’ conform to ‘PersistentModel’
-
Instance method ‘getValue(forKey:)’ requires that ‘Timer’ conform to ‘PersistentModel’
I know this means something along the lines of “Cannot save Timer
to SwiftData”, but I’m not sure how I can resolve this problem I’m facing.
My stopwatch app would have the ability to create and control multiple stopwatches, which includes global control (Start all, stop all, reset all etc.) and the ability to save stopwatch sets to refer to at a later time.
Solutions I tried
1. ChatGPT 4o suggested solution: Define a separate StopwatchController
which has the functions start()
, stop()
, reset()
and contains a timer property.
Why is this not ideal: I find that I cannot globally control the stopwatches this way
2. Defining stopwatch control functions in StopwatchItemView
, which is shown in ContentView
in the form of
forEach(stopwatches) { stopwatch in
StopwatchItemView(stopwatch: stopwatch)
}
Why this is not ideal: Unable to globally control stopwatches, harder to persist timer status/progress when app is quit
Code
Note: Including ContentView is not relevant as it still features the boilerplate code.
StopwatchController.swift
import Foundation
import SwiftData
import Combine
@Model
class StopwatchSet: Identifiable, ObservableObject {
var id = UUID()
var label: String
var stopwatches: [StopwatchItem]
init(id: UUID = UUID(), label: String, stopwatches: [StopwatchItem]) {
self.id = id
self.label = label
self.stopwatches = stopwatches
}
}
@Model
class StopwatchItem: Identifiable, ObservableObject {
var id = UUID()
var isRunning: Bool = false
var label: String
var timeElapsed: Double = 0.0
var laps: [Lap] = []
private var timer: Timer?
init(id: UUID = UUID(), label: String, timeElapsed: Double) {
self.id = id
self.label = label
self.timeElapsed = timeElapsed
}
func start() {
isRunning = true
timer = Timer.scheduledTimer(withTimeInterval: 0.01, repeats: true) { [weak self] _ in
self?.timeElapsed += 0.01
}
}
func stop() {
isRunning = false
timer?.invalidate()
timer = nil
}
func reset() {
stop()
timeElapsed = 0.0
}
}
@Model
class Lap: Identifiable, ObservableObject {
var id = UUID()
var label: String
var timeElapsed: String
init(label: String, timeElapsed: String) {
self.label = label
self.timeElapsed = timeElapsed
}
}
StopwatchItemView.swift
import SwiftUI
import SwiftData
struct StopwatchItemView: View {
@State var stopwatch: StopwatchItem
var body: some View {
VStack {
Text(formatTime(stopwatch: stopwatch, input: stopwatch.timeElapsed))
}
}
}
#Preview {
StopwatchItemView(stopwatch: StopwatchItem(label: "Stopwatch", timeElapsed: 0.0))
}
Ethan Lim is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.