Nelson.J Nelson.J - 1 year ago 49
Swift Question

How does one use an NSTimer to update a label for elapsed time in minutes and seconds?

I have the timer counting down but I'm having trouble with:

  1. Getting the timer to start over continuously once it hits 0

  2. Having the countdown like this: 4:00, 3:59, 3:58... rather than 240, 239, 238...

Here is my code:

ViewController: UIViewController {

@IBOutlet weak var incomeTimeLabel: UILabel!
var timer: NSTimer!
let startIncomeTime = 240
var incomeTime = 240

func startTimer() {
timer = NSTimer.scheduledTimerWithTimeInterval(1.0, target: self,
selector: #selector(ViewController.timerStarted),
userInfo: nil, repeats: true)

func timerStarted() {

override func viewDidLoad() {

func incomeTimer() {
if incomeTime > -1 {
incomeTimeLabel.text = String(incomeTime--)
} else if incomeTime == -1 {
incomeTimeLabel.text = String(startIncomeTime)

Ive been teaching myself to code for only a few months but I feel like theres a more efficient way to do this, so any other advice you think would help would be greatly appreciated.

Answer Source

You seem to be confused about what an NSTimer is. It's not a "timer" like a stopwatch. It provides a means for executing a method asynchronously at certain intervals (eg. "call this method every 1 second").

While the following is a little overkill, it uses MVC to illustrate the point. Code example taken from the "Teaching App Development with Swift" Stopwatch project.

Model Stopwatch.swift:

import Foundation

class Stopwatch {

    private var startTime: NSDate?

    var elapsedTime: NSTimeInterval {
        if let startTime = self.startTime {
            return -startTime.timeIntervalSinceNow
        } else {
            return 0

    var elapsedTimeAsString: String {
        return String(format: "%02d:%02d.%d",
            Int(elapsedTime / 60),
            Int(elapsedTime % 60),
            Int(elapsedTime * 10 % 10))

    var isRunning: Bool {
        return startTime != nil

    func start() {
        startTime = NSDate()

    func stop() {
        startTime = nil


Controller ViewController.swift:

import UIKit

class ViewController: UIViewController {

    @IBOutlet weak var elapsedTimeLabel: UILabel!
    let stopwatch = Stopwatch()

    override func viewDidLoad() {

    override func didReceiveMemoryWarning() {

    @IBAction func startStopwatch(sender: UIButton) {
        NSTimer.scheduledTimerWithTimeInterval(0.1, target: self,
            selector: "updateElapsedTimeLabel:", userInfo: nil,
            repeats: true)

    @IBAction func stopStopwatch(sender: UIButton) {

    func updateElapsedTimeLabel(timer: NSTimer) {
        if stopwatch.isRunning {
            elapsedTimeLabel.text = stopwatch.elapsedTimeAsString
        } else {


In other words, you need to do your "time math" with NSDate objects, not Ints.

To obtain feature #1 you mention in your question, you could change the logic in updateElapsedTimeLabel: to call a function when the difference between the stopwatch.elapsedTime and an NSDate "four minutes from now" is 0.

To obtain feature #2, of counting down, simply add a method timeLeft that returns the difference between an NSDate "four minutes from now" and the elapsedTime.

The point here is to understand how NSTimer works, how to use NSDate to calculate the difference between two moments in time, and how to use format strings to display what you want. There's enough raw material here for you to implement your own requirements.