swiftswiftuitimer

Is it necessary to cancel timer created by Timer.publish, in SwiftUI?


I inherited a code that is creating SwiftUI timer as below, inside a view struct.

struct CustomButton: View    {
    ...
    private let timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
    ...

And do not see any code to cancel the timer once the intended trigger is achieved and work is done. But looking at the below guide I se that it is suggested to cancel the timer using timer.upstream.connect().cancel()

My question is how important is to cancel this timer, and I dont see any issues faced by the team till now but is this something that can cause any kind of issues? Or if the view gets released the timer will get released so this is not that important?

Reference: https://www.hackingwithswift.com/books/ios-swiftui/triggering-events-repeatedly-using-a-timer


Solution

  • let timer = Timer... is actually not a good practice with SwiftUI and you could end up with memory leaks since SwiftUI can recreate the View at any time and create multiple timers.

    The "best" way to handle animations and such is with TimelineView

    TimelineView(.periodic(from: startDate, by: 1)) { context in
         AnalogTimerView(date: context.date)
    }
    

    https://developer.apple.com/documentation/swiftui/timelineview

    But if you have to use Timer you should use it with onReceive so it gets destroyed appropriately.

     .onReceive(Timer.publish(every: 1, on: .main, in: .common).autoconnect()) { output in
        print("Timer called")
    }
    

    If you can also move into async/await and start using Task as an alternative.

    struct TimerSample: View {
        @State private var isDisabled: Bool = true
        var body: some View {
            Button("Hello World!") {
                
            }.disabled(isDisabled)
                .task(id: isDisabled) {
                    guard isDisabled else {return}
                    try? await Task.sleep(for: .seconds(2))
                    isDisabled.toggle()
                }
        }
    }