I have a Timer
object within my view:
@State var timer: Timer?
Inside the view's body, I start the timer when the value of popup
changes to true
, and I invalidate the timer when viewModel.present
changes to true
.
var body: some View {
/* ... */
.onChange(of: viewModel.popup) { popup in guard popup else { return }; setupTimer() }
.onChange(of: viewModel.present) { present in guard present else { return }; resetTimer() }
}
In setupTimer()
, I create the timer instance:
private func setupTimer() {
guard timer == nil else { return }
timer = Timer.scheduledTimer(withTimeInterval: 8.0, repeats: false, block: { _ in
// This code won't stop running!
})
}
Next, in resetTimer()
, I invalidate and remove the timer:
private func resetTimer() {
timer?.invalidate()
timer = nil
// ...
}
But the timer continues to run. I'm not sure why timer?.invaludate()
fails to work; I've used breakpoints to assure that
setupTimer()
is indeed only called once such that only a single timer is running (as per the answer here)self.timer
is non-nil
when invalidating withinresetTimer
(as per the answer here)
Any ideas on what is going wrong here? I'm running this program in macOS 12.2.1.
Edit: Ok, so after some investigation, I've seemed to narrow down the problem. It seems as though the timer is successfully invalidated when setupTimer()
is called within a tap gesture or action by the user. However, if setupTimer()
is called "automatically" within a view (i.e. via onAppear
, onChange
, etc.) then it cannot be invalidated. So the new question I have is: why is this? Why can I not call setupTimer()
within onAppear
/onChange
and cancel the timer?