Passing a timer to a child view in SwiftUI

Viewed 552

In my top level view, I have declared a timer like so:

Struct ContentView: View  {
    @State var timer = Timer.publish(every: 1, on: .main, in: 
     .common).autoconnect()

    var body: some View {
            ZStack {
                if self.timerMode == .warmup {
                    WarmupView(
                        timer: $timer
                    )
                if self.timerMode == .work {
                    WorkView(
                        timer: $timer
                    )
       }
    }
}

In a child view, I want to be able to access and update this timer, which will serve as the single source of truth.

Struct WarmupView: View {
  @Binding var timer: Publishers.Autoconnect<Timer.TimerPublisher>
  @Binding var timeRemaining: Int

  var body: some View {
    VStack {
      Text("\(self.timeRemaining)").font(.system(size: 160))
                            .onReceive(self.timer) { _ in
                            if self.timeRemaining > 0 {
                                self.timeRemaining -= 1
                            }
                        }
    }
  }
}

The timer is publishing to the warmup view without issues, but when timerMode is updated to .work (which has nearly identical code) and the view changes, the timer stops publishing.

1 Answers

Simple as changing the type of your @Binding var timer in your WarmupView to Publishers.Autoconnect<Timer.TimerPublisher>. The .autoconnect() wraps the timer publisher in another publisher, which changes the type.


Here's a simplified version of your code:

struct ContentView: View  {
    let timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
    @State var remaining = 100

    var body: some View {
        Text("\(remaining)")
            .font(.system(size: 160))
            .onReceive(timer) { _ in
                if self.remaining > 0 {
                    self.remaining -= 1
                }
            }
    }
}
Related