Home > Software engineering >  Timer publisher init timer after button click
Timer publisher init timer after button click

Time:07-28

In all tutorials and in official documentation I only see initialization of timer straight up when the view loads.

@State private var timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()

and later on

.onReceive(timer) {....}

but how should I init timer only on button click and assign it to the unassigned / not connected timer.

Later on I will need to cancel and reconnect, but that is not the issue here. Issue here is connecting only after button click.

I tried to init it like this

@State private var timer: Cancellable?

....

timer = Timer.publish(every: 1, on: .main, in: .common).connect()

But I can not call onReceive on timer inited like this, because first:

Protocol 'Cancellable' as a type cannot conform to 'Publisher'

and second

Argument type 'Cancellable?' does not conform to expected type 'Cancellable'

CodePudding user response:

Just put the timer in a child view and control its visibility with a bool. When the TimerView is removed the state is destroyed and the timer stops.

struct ContentView: View {
    @State var started = false

    var body: some View {
        VStack {
            Button(started ? "Stop" : "Start") {
                started.toggle()
            }
            if started {
                TimerView()
            }
        }
    }
}

struct TimerView: View {

    @State private var timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()

    ...

CodePudding user response:

Here is demo of possible approach - timer publisher is created with button (same autoconnected), but subscriber should be registered conditionally, because timer in such case is an optional.

Tested with Xcode 13.4 / iOS 15.5

demo

struct ContentView: View {

    @State private var timer: Publishers.Autoconnect<Timer.TimerPublisher>? // << here !!

    @State private var value = 10 // just demo

    var body: some View {
        Button {
            if timer != nil {
                timer = nil     // << reset !!
                value = 10
            } else {
                timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()    // << create !!
            }
        } label: {
            Text("Toggle")
        }
        if timer != nil {              // << verify !!
            Text("Counter: \(value)")
                .font(.largeTitle)
                .onReceive(timer!) { _ in     // << observe !!
                    value = value == 0 ? 10 : value - 1
                }
        }
    }
}
  • Related