I'm currently prototyping a timer app in Swift Playgrounds.
The code for the top-level view and the child view is the following:
The code for the TimerManager class is the following:
The bug happens doing the following:
How can I fix this bug?
The code for the top-level view and the child view is the following:
Code Block swift import SwiftUI import Foundation import PlaygroundSupport struct MultiTimerView: View { @State private var timerList = [TimerManager]() var body: some View { VStack(spacing: 20){ ForEach(timerList.indices, id: \.self) { id in SingleTimerView(timerManager: self.$timerList[id]) } Spacer() Button(action:addTimer){ Image(systemName:"plus") } } } func addTimer(){ timerList.append(TimerManager()) } } struct SingleTimerView: View { @Binding var timerManager : TimerManager var body: some View { HStack(spacing: 20){ Spacer() Stepper("\(timerManager.secondsLeft)", value: $timerManager.secondsLeft, in: 0...1000) Spacer() Button(action:self.timerManager.toggleTimer){ Image( systemName: self.timerManager.timerRunning ? "pause.fill" : "play.fill") } Spacer() } } } PlaygroundPage.current.setLiveView(MultiTimerView().padding(50))
The code for the TimerManager class is the following:
Code Block swift import SwiftUI import Foundation import PlaygroundSupport public class TimerManager: ObservableObject,Identifiable{ @Published public var timerRunning = false @Published public var secondsLeft = 0 var timer = Timer() public var id = UUID() public init() {} public func toggleTimer(){ if timerRunning == false { timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true , block: { timer in if self.secondsLeft == 0 { self.timerRunning = false self.secondsLeft = 0 timer.invalidate() } else { self.secondsLeft-=1 } }) } else { timer.invalidate() } timerRunning.toggle() } }
The bug happens doing the following:
create a timer with the + button
load the timer a given amount of seconds with its stepper on the right side
press its play button
wait a couple of seconds and then add another timer
How can I fix this bug?
You have made your TimerManager an ObservableObject, but your SingleTimerView does not handle it as an ObservedObject.
Fix:
In SingleTimerView, change
@Binding var timerManager : TimerManager
to
@ObservedObject var timerManager : TimerManager
In MultiTimerView, change
SingleTimerView(timerManager: self.$timerList[id])
to
SingleTimerView(timerManager: self.timerList[id])
Fix:
In SingleTimerView, change
@Binding var timerManager : TimerManager
to
@ObservedObject var timerManager : TimerManager
In MultiTimerView, change
SingleTimerView(timerManager: self.$timerList[id])
to
SingleTimerView(timerManager: self.timerList[id])