Search code examples
swiftswiftui

Update view on a real minute change


I am trying to update view every real minute change. I can't find any Notification publishers that can trigger that, so I have an idea to start timer when next minute changing, but I don't really know how to do it( I've tried this code, not seems to work:

.onChange(of: Calendar.current.component(.minute, from: Date())) { _ in
                print("Minute has Changed")
                
                if calendarViewModel.isTimerAlreadyPublishing {
                    print("Timer continue publishing")
                } else {
                    calendarViewModel.startTimer()
                }
            }

Maybe someone can say more methods how can I accomplish that goal?


Solution

  • You can achieve this with Timer, starting it when actual minute changes in current time zone and updating view every 60 seconds:

    struct ContentView: View {
        @State var currentDate = Date()
        @State var timer: Timer?
    
        var body: some View {
            Text("Current minute: \(currentDate.minute)")
                .onAppear {
                    startMinuteTimer()
                }
            
                .onChange(of: currentDate) {_ in
                    //this is called when every real minute changes in current TimeZone
                }
    
        }
    
        private func startMinuteTimer() {
            // Get the number of seconds to the next minute
            let seconds = 60 - Calendar.current.component(.second, from: .now)
            // Set the initial timer to sync with the next minute change
            DispatchQueue.main.asyncAfter(deadline: .now() + Double(seconds)) {
                self.currentDate = Date()
                // After the first trigger, set a timer to repeat every minute
                self.timer = Timer.scheduledTimer(withTimeInterval: 60, repeats: true) { _ in
                    self.currentDate = Date()
                }
            }
        }
    }
    
    extension Date {
        var minute: Int {
            return Calendar.current.component(.minute, from: self)
        }
    }
    
    #Preview {
        ContentView()
    }