A class must set all its properties to sensible values in its init() method, and you cannot refer to self in the init() method before all the properties are assigned sensible values.
So, how does one go about having a Timer as a property of a class that calls an instance method of that class when it fires?
e.g.
import Foundation
class RepeatingThing {
var aTimer: Timer
let timerDuration = 2.0
init() {
aTimer = Timer.scheduledTimer(withTimeInterval: timerDuration, repeats: true, block: {_ in self.timerDoSomething() } )
}
func timerDoSomething() {
print("The timer fired!")
}
}
let repeater = RepeatingThing()
Doesn't compile due to: 'self' captured by a closure before all members were initialized
I can't initialise the member without using a closure, but I can't use a closure because I haven't initialised the member.
Noting that you are declaring your aTimer property as a variable, I would point out that you can solve this example by changing that line thusly: var aTimer: Timer? = nil
Personally, I have elected to use DispatchSourceTimer instead in a similar situation, as its scheduling is separate from its initialization.
Thanks - I considered that but shied away as the idea of having to unwrap an optional every time I wanted to refer to the timer (which I could guarantee would be non-nil.) I suppose one could make a 'shadow' optional Timer and then have a computed timer property which force unwraps the shadow timer....
Another "possibility" is to assign a bogus Timer: var aTimer = Timer(fire: .distantFuture, interval: 0, repeats: false, block: { _ in })
(and then invalidate before re-assigning, just to be safe.)
Personally, I have elected to use DispatchSourceTimer instead in a
similar situation, as its scheduling is separate from its
initialization.
Separating initialisation and scheduling isn’t the issue here, because Timer supports that just fine:
let t = Timer(fire: Date(), interval: 1.0, repeats: true, block: {
… your code here …
})
RunLoop.current.add(t, forMode: .default)
The difference is that DispatchSourceTimer lets your set the event handler separate from creating the source, whereas Timer does not.
Personally I’m not a fan of the design approach outlined in the first post here: If I create a class that’s active (runs a timer, hits the network, whatever), I never want it to start on initialisation. It is much better, in my experience, to separate initialisation from scheduling. And once you do that, you have to track whether the timer is running or not, at which point having an optional Timer is the right answer.
One reason I want the separation between initialisation and scheduling is that you need to separate descheduling from deinitialisation. When dealing with active classes, relying on the deinit to shut things down is a really bad idea.