Create a timer that runs every second, on the second?


I have a countdown timer to a specific date. It looks pretty good, and updates each second to show the countdown. Here is the code I'm using to create the timer:

func scheduleTimeLabelsUpdateTimer() {

    var components = Calendar.current.dateComponents([.day, .hour, .minute, .second], from: Date())
    components.second! += 1

    let nextSecondDate = components)!

    let timer = Timer(fireAt: nextSecondDate, interval: 1, target: self, selector: #selector(updateTimeLabels), userInfo: nil, repeats: true)

    RunLoop.main.add(timer, forMode: .commonModes)


However, I'd like it to update each second, on the second, so that the clock updates at the same time as each second passes by. Right now it updates each second from when this method is called, which is in viewDidLoad().

For example if the countdown is set for midnight, I want it to hit zero exactly at midnight. Right now it may hit zero slightly after midnight, depending on how far into the second it was when the user opened this screen.

EDIT: This is how the countdown is displayed to the user. updateTimeLabels() just sets the text for each of those labels based on the amount of time left until that date. I would like each of the labels to be updated exactly on each second. This way the countdown will "hit zero" exactly on time. Notice how right now, the number of seconds hits zero, and then the system clock on the status bar updates. I would like these to happen at the same time.

This code, which I found somewhere on Stack Overflow many months ago, is being called in updateTimeLabels() to calculate the remaining time:

public func timeOffset(from date: Date) -> (days: Int, hours: Int, minutes: Int, seconds: Int) {
    // Number of seconds between times
    var delta = Double(self.seconds(from: date))

    // Calculate and subtract whole days
    let days = floor(delta / 86400)
    delta -= days * 86400

    // Caluclate and subtract whole hours
    let hours = floor(delta / 3600).truncatingRemainder(dividingBy: 24)
    delta -= hours * 3600

    // Calculate and subtract whole minutes
    let minutes = floor(delta / 60.0).truncatingRemainder(dividingBy: 60)
    delta -= minutes * 60

    // What's left is seconds
    let seconds = delta.truncatingRemainder(dividingBy: 60)

    return (Int(days), Int(hours), Int(minutes), Int(seconds))

In your code, you never use nanosecond when you init your component

So the timer will always hit at the round second number.

I test your code below print the Date() in the selector

I'm not sure if this is what you are talking about "hit zero", hope this would help