NSTimer in Swift

In this tutorial we are going to learn to control time :)
We are going to do that by making a binary counter using NSTimer in Swift.

The final result will look like this:

Final Result

You can get the full code here.

First let’s look a bit a the pieces that we are going to use.

NSTimer

Basically a timer executes some code after a time interval one or more times. With NSTimer you create timer objects. A timer object waits for a specific time interval to elapse and then it fires. When it fires, the timer sends a message to the target object associated with it.

In order to truly understand timers, you have to know a bit about NSRunLoop.

NSRunLoop

Each iOS application creates at least one NSThread object (the Main Thread) which represents the thread of execution of the application code. Each NSThread object has an associated NSRunLoop that manages input sources such as: mouse, keyboard, touches, ports, connections and timers.

NSRunLoop continuously executes a loop in which it checks for input events. One of the events that is checked for is the ‘timer interval elapsed event’. When the run loop detects this kind of event it will fire the method on the target associated with the timer.

Timers don’t work in real-time. The reason: there is a point in the loop (NSRunLoopMode) in which the timer events are managed. The run loop can spend more time than the timer interval in other modes so the timer might get delayed.

The Binary Counter

Let’s do this!

Step 1: Create a new project

Open Xcode 6, create a new “Single Page Application” and select Swift as the programming language.

enter image description here

Step 2: Create the UI

Open Main.storyboard.

  1. Drag one UILabel and two UIButton objects from the Object Library in the Utilities panel. Name the first button “Start” and the second button “Resetenter image description here
  2. In the Identity inspector, set the class of the single view controller on screen to ViewController.enter image description here
  3. In the Attributes inspector, set the Simulated Metrics -> Size to IPhone 4 inch.enter image description here

Don’t forget to build the project after this step, to ensure that everything works fine.

Step 3: Add outlets and actions

Open Main.storyboard and also open ViewController.swift in the Assistant Editor (with the Main.storyboardopened, press and hold the Option Option () button and then click on ViewController.swift). Then to create the Outlet for the label and Actions for buttons: Right Click + Drag from the label/buttons to the ViewController.swift, inside the class definition.

enter image description here

class ViewController: UIViewController {

    @IBOutlet var labelForBinaryCount: UILabel!

    ...

    @IBAction func start() {

    }

    @IBAction func reset() {

    }

}

Step 4: Add the NSTimer object

Under the IBOutlet declaration add two new variables: timer and binaryCount:

class ViewController {
    ...

    @IBOutlet var labelForBinaryCount: UILabel!

    var timer       = NSTimer()
    var binaryCount = 0b0000

    ...
}

In Swift, numbers can be represented in various formats by prefixing the digits: binary (0b), octal (0o) and hexadecimal (0x). For example, the number 0b0011 is the binary representation of the number 3.

Now lets create a new NSTimer that will:

  • fire after 1 second
  • execute the function “countUp()
  • repeat at 1 second time intervals

Modify the start() function:

class ViewController {
    ...

    @IBAction func start() {

        timer = NSTimer.scheduledTimerWithTimeInterval(1.0, target: self, selector: "countUp", userInfo: nil, repeats: true)
    }

    ...
}

Using scheduledWithTimeInterval(_:target:selector:userInfo:repeats:) class method to create the timer will also add the timer automatically to the NSRunLoop associated with the NSThread in which the timer is created.

The alternative is to create the timer using an initializer and to manually add it to the current NSRunLoop:

class ViewController {
    ...

    @IBAction func start() {

        //timer = NSTimer.scheduledTimerWithTimeInterval(1.0, target: self, selector: "countUp", userInfo: nil, repeats: true)

        timer = NSTimer(timeInterval: 1.0, target: self, selector: "countUp", userInfo: nil, repeats: true)
         NSRunLoop.currentRunLoop().addTimer(timer, forMode: NSRunLoopCommonModes)
    }

    ...
}

See NSRunLoopModes for other modes in which you can attach the timer.

Now lets create the countUp() function and also a helper function that will update the label:

class ViewController {
    ...

    func countUp() {

        binaryCount += 0b0001
        // if the counter reached 16, reset it to 0
        if (binaryCount == 0b10000) { binaryCount = 0b0000 }

        updateText()
    }

    // Update the text from the label, by always maintaining 4 digits.
    func updateText() {

        // Convert from Binary to String
        var text = String(binaryCount, radix:2)

        // Pad "0" to the left, to always have 4 binary digits
        for i in 0..<4 - count(text) {
            text = "0" + text;
        }

        labelForBinaryCount.text = text
    }

    ...
}

Notice the call String(binaryCount, radix:2). This call creates a string using the Int stored in binaryCount, but by representing it in base 2 (binary representation)

And finally, in order to stop and reset the counter, let’s implement the method reset():

class ViewController {
    ...

    @IBAction func reset() {

        timer.invalidate()
        binaryCount = 0b0000

        updateText()
    }

    ...   
}

In order to stop the timer, the method invalidate() has to be called on it. After this method executes, the timer stops and becomes invalid (there is no way to start it again). In order to start the timer again, a new NSTimer instance needs to be created, as you can see in the start() function.

FinalResultEnd

You can get the code here.

Challenges

  • Make the binary counter countdown from 1111 to 0000
  • Create a timer that individually controls each digit of the 4-bit binary counter
  • use a timer to animate a view
  • use a timer to end game rounds
  • Add a timer that executes once after the main timer is reset and displays the final count in decimal.
  • Create a timer object that accepts closures instead of the target-action mechanism (Hint: create a wrapper class around an NSTimer instance)
  • Create a timer that accepts closures, without creating a wrapper around NSTimer (Hint: seeCFRunLoopTimerCreateWithHandler)

If you found this useful, please take a moment and share it with your friends :)

  8 comments for “NSTimer in Swift

  1. Eric
    September 21, 2015 at 1:37 am

    How would I change this to count up in a normal way. Not hexidecimal, binary or Octogonal?

    • Raul Pop
      September 21, 2015 at 7:09 am

      You can change the updateText() function to:
      func updateText() {

      labelForBinaryCount.text = “\(binaryCount)”
      }
      The binaryCount variable is the actual normal count. Build and run and you will see the result.

      Binary, Hexa, Octal are only representations of the same number.
      For example if you want to assign the number 3 to this variable you can do it like this:
      binaryCount = 3 (decimal)
      binaryCount = 0b0011 (binary)
      binaryCount = 0o3 (octal)
      binaryCount = 0x3 (hexa)

      So as one last step you can rename the “binaryCount” variable to “timerCount” and change all 0b…. values to their decimal equivalents and it will work the same.

      Using Hexa instead of Decimal for the same variable is sometimes needed. For example, many designers will give you the colors for a design in their hexadecimal representation. (0xff0000 == Red color).

      • Eric
        September 25, 2015 at 5:57 pm

        Thanks that works. You should open up a help forum on the site!

  2. Norval
    October 17, 2015 at 2:09 am

    for i in 0..<4 – count(text) {
    using swift im getting an error:
    cannot invoke count with an argument list of type (string)

    • October 26, 2015 at 12:46 pm

      in swift 2.0 you get the number of characters like this: text.characters.count

  3. Dennis
    February 19, 2016 at 2:01 am

    Really enjoyed this article and got me working quickly with Swift. Well done!

Leave a Reply

Your email address will not be published. Required fields are marked *

Subscribe
We send about one email per week with our latest tutorials and updates
Never display this again :)