swiftswiftuipublishercombine

Create a Timer Publisher using Swift Combine


I've been watching the Data Flow Through SwiftUI WWDC talk. They have a slide with a sample code where they use a Timer publisher that gets connected to a SwiftUI View, and updates the UI with the time.

I'm working on some code where I want to do the exact same thing, but can't figure out how this PodcastPlayer.currentTimePublisher is implemented, and then hooked to the UI struct. I have also watched all the videos about Combine.

How can I achieve this?

The sample code:

struct PlayerView : View {
  let episode: Episode
  @State private var isPlaying: Bool = true
  @State private var currentTime: TimeInterval = 0.0

  var body: some View {
    VStack { // ...
      Text("\(playhead, formatter: currentTimeFormatter)")
    }
    .onReceive(PodcastPlayer.currentTimePublisher) { newCurrentTime in
      self.currentTime = newCurrentTime
    }
  }
}

Solution

  • Here you have an example of a Combine timer. I am using a global, but of course you should use whatever is applicable to your scenario (environmentObject, State, etc).

    import SwiftUI
    import Combine
    
    class MyTimer {
        let currentTimePublisher = Timer.TimerPublisher(interval: 1.0, runLoop: .main, mode: .default)
        let cancellable: AnyCancellable?
    
        init() {
            self.cancellable = currentTimePublisher.connect() as? AnyCancellable
        }
    
        deinit {
            self.cancellable?.cancel()
        }
    }
    
    let timer = MyTimer()
    
    struct Clock : View {
      @State private var currentTime: Date = Date()
    
      var body: some View {
        VStack {
          Text("\(currentTime)")
        }
        .onReceive(timer.currentTimePublisher) { newCurrentTime in
          self.currentTime = newCurrentTime
        }
      }
    }