Home > OS >  @Publish counters not in sync inside timer SwiftUI
@Publish counters not in sync inside timer SwiftUI

Time:04-20

I'm working on an app where I'm using a timer to count down time left in a workout and also count up for total time. My counters are out of sync, looks like it's less than a second off. I'm wondering if it has something to do with @Publish, maybe one fires before the other. Any idea what's happening and how to fix it?

class TimeManager: ObservableObject {

   @Published var totalTime: Double = 0.0
   @Published var timeRemaining: Double = 180.0

   DispatchQueue.main.asyncAfter(deadline: DispatchTime.now()   2.0) {
      self.timer = Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true) { [weak self] timer in
         guard let self = self else { return }
         
         self.timeRemaining -= 0.1
         self.totalTime  = 0.1
     }
   }
}

then my view

@ObservedObject var timeManager = TimeManager()
 ...

var body: some View {
    
    VStack {
       let time = timeManager.timeRemaining
       let minutes = Int(time) / 60 % 60
       let seconds = Int(time) % 60
            
       ZStack {
          Progress()
          Text(String(format:"i:i", minutes, seconds))
          .font(.system(size: 60))
       }
            
       let total = timeManager.totalTime
       let totalMins = Int(total) / 60 % 60
       let totalSecs = Int(total) % 60

       Text(String(format:"i:i", totalMins, totalSecs))
       .font(.system(size: 40))
   }
}

CodePudding user response:

Maybe calculate your 2nd value based on the first when you decrement the time. For example like this: remaining = (180 - total) >= 0 ? (180 - total) : 0

CodePudding user response:

Your time values are in sync. The reason for the behaviour you are seeing is the Double / Int conversions and the rounding applied while display the Texts. Try this line:

Text("\(timeManager.timeRemaining   timeManager.totalTime)")

and you will see this allways adding up to 180.

You could try Int values in your Viewmodel decrementing/incrementing by 1 and a DateComponentsFormatter to format the values in your View.

let componentsFormatter = DateComponentsFormatter()


Text("\(componentsFormatter.string(from: Double(timeManager.timeRemaining)) ?? "NAN")")
              .font(.system(size: 60))

You would of course need to tweek the formatter to display the time the way you want it to be. But I agree with Paulw11. This seems like a bad design. It would be better to have a single source of truth as a Date and go from there.

  • Related