April 02, 2016

How to Add a Countdown Timer with Day + Hour + Minute + Second In Swift

There are various reasons why one would want a countdown timer. In my case, I wanted a countdown timer to display the time left before an auction closes. Naturally, I searched for available packages that I could use instantly first. And I found MZTimerLabel.

It is a pretty handy package. Lots of websites have recommended it even. Almost perfect for my use case… But then I discovered that if I choose to display the day info, MZTimerLabel will always show an extra day. A few users have reported that bug. No response.

Plan B

Obviously having a wrong day displayed is a deal breaker. For my plan b, I have no choice but to come up with my own solutions since I can’t find any other package to use.

To my surprise, it is extremely easy!


Solution in Swift

There are two scenarios here:

Scenario A: When You Are Counting Down to a Deadline

If you have an deadline that you need to count to, this method takes the deadline as a UNIX Timestamp input and starts the timer for you.

@IBOutlet weak var timeLeftLabel: UILabel!

var timeEnd: NSDate!

func updateView() {
    // Initialize Label
    timeEnd = NSDate(timeIntervalSince1970: timestamp)
    setTimeLeft()

    // Start timer
    NSTimer.scheduledTimerWithTimeInterval(1.0, target: self, selector: #selector(self.setTimeLeft), userInfo: nil, repeats: true)
}

func setTimeLeft() {
    let timeNow = NSDate()

    // Only keep counting if timeEnd is bigger than timeNow
    if timeEnd.compare(timeNow) == NSComparisonResult.OrderedDescending {
        let calendar = NSCalendar.currentCalendar()
        let components = calendar.components([.Day, .Hour, .Minute, .Second], fromDate: timeNow, toDate: timeEnd, options: [])

        var dayText = String(components.day) + "d "
        var hourText = String(components.hour) + "h "

        // Hide day and hour if they are zero
        if components.day <= 0 {
            dayText = ""
            if components.hour <= 0 {
                hourText = ""
            }
        }
        timeLeftLabel.text = dayText + hourText + String(components.minute) + "m " + String(components.second) + "s"

    } else {
        timeLeftLabel.text = "Ended"
    }
}

Scenario B: When You Just Want to Countdown By X Hours / Minutes / Seconds

With slight modifications of our previous codes, we can make it work for Scenario B too.

@IBOutlet weak var timeLeftLabel: UILabel!

// Counting down to 10 hours, converted to seconds
let timeEnd = NSDate(timeInterval: 10*60*60, sinceDate: NSDate())

func updateView() {
    // Initialize the label
    setTimeLeft()

    // Start timer
    NSTimer.scheduledTimerWithTimeInterval(1.0, target: self, selector: #selector(self.setTimeLeft), userInfo: nil, repeats: true)
}

func setTimeLeft() {
    let timeNow = NSDate()

    // Only keep counting if timeEnd is bigger than timeNow
    if timeEnd.compare(timeNow) == NSComparisonResult.OrderedDescending {
        let calendar = NSCalendar.currentCalendar()
        let components = calendar.components([.Day, .Hour, .Minute, .Second], fromDate: timeNow, toDate: timeEnd, options: [])

        var dayText = String(components.day) + "d "
        var hourText = String(components.hour) + "h "

        // Hide day and hour if they are zero
        if components.day <= 0 {
            dayText = ""
            if components.hour <= 0 {
                hourText = ""
            }
        }
        timeLeftLabel.text = dayText + hourText + String(components.minute) + "m " + String(components.second) + "s"

    } else {
        timeLeftLabel.text = "Ended"
    }
}

That's all!

***