Swift Fun with Timers and GCD

Today’s challenge is to create a timer app. This is a challenge that I received as part of a job interview, with a time constraint of 5 minutes. I like the idea and think this is a nice example of how we can use NSTimer in Swift. The app is supposed to display the time in the hours, minutes and seconds format.

There are a few ways you could accomplish this. The first idea I had was to have a timer and a method that updates a label every second. This is probably the simplest way to do it, without trying to optimise the implementation. I will also talk about a nice way to do this without a timer, but for now let’s implement the simple approach.

Let’s start by creating a project in Xcode. It will be a “Single View Application”:

timerProject

Now go to the Main storyboard and add a label. Add an outlet for the label so we can update its text:

timeLabel

Great, we have a label, now let’s find a way to make it show the time. For this we need the NSDate and NSDateFormatter. We’ll add our code in viewDidLoad: method first and then evaluate if we should move it or not.

In Objective C, we could get a NSDate object initialised with the current date and time by calling the date convenience method:

[NSDate date];

In Swift it’s enough to use the designated initialiser and you will have the same result:

let now = NSDate()

Next we need to create a date formatter.

let dateFormatter = NSDateFormatter()
dateFormatter.timeStyle = .MediumStyle;

Notice that we are using the same APIs we used in Objective C. There are no Swift specific classes to handle date so we are using the Cocoa ones. Use the date formatter to set the text of the label:

timeLabel.text = dateFormatter.stringFromDate(now)

And now the label will display the time but it won’t update. We haven’t created our timer yet so this is to be expected. Let’s create the timer:

NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: "updateLabel", userInfo: nil, repeats: true)

This line of code creates a timer with a time interval of 1 second. It will call the method “updateLabel” on the target (which is set as self). We have also set the repeats property to true because we want it to fire every second, not just once. Notice that in order to specify a selector in Swift it’s enough to just pass the name of the method. The method used to create the timer will also add it to a run loop. Run loops maintain a strong reference to their timers, so we don’t need to have a reference for our timer unless we want to invalidate it at some point. This is not the case in our app, we want the timer to run for the whole duration of our apps life cycle.

All that’s left to do is implement the updateLabel method and we’ll have finished our challenge. We have already written code to create the NSDate object, create the NSDateFormatter and set the text of the label. Move all that in the updateLabel method.

Run the app and notice that the label will be updated and will start showing the time. We should improve this a little bit. First, when the label appears you can still see the placeholder text from the storyboard. And second, this creates a new date formatter every time the method is called. Creating a date formatter is not a cheap operation and Apple tells us we should cache a date formatter rather than create it every time. Let’s create a constant for the date formatter and move the code that sets it back to the viewDidLoad: method.

The whole class will look like this:

class ViewController: UIViewController {

    @IBOutlet weak var timeLabel: UILabel!
    let dateFormatter = NSDateFormatter()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        dateFormatter.timeStyle = .MediumStyle;
        
        let now = NSDate()
        timeLabel.text = dateFormatter.stringFromDate(now)
        
        NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: "updateLabel", userInfo: nil, repeats: true)
    }

    func updateLabel() {
        let now = NSDate()
        
        timeLabel.text = dateFormatter.stringFromDate(now);
    }
}

While this is a really simple example, it does teach us how to user timers and how to handle date in Swift. As I said when I was explaining my approach, there are different ways to do this. Another way, which doesn’t even have to use timers, is by using recursion. In the updateLabel method you can use performSelector: and call the same method with a one second delay. So you can call the method once from viewDidLoad: and then it will call itself again and again and again. I thought it is a clever idea. The only question remaining is: how do use do performSelector in Swift. Turns out it’s not as straight forward as you’d think.

There is no equivalent for the performSelector method in Swift. So you are left with 2 options: use timers (this is what we have already seen) or use GCD. After a bit of research I found that this is a hot topic on Stack Overflow. We could use the delay method from the answer and modify our implementation to look like this:

func updateLabel() {
    let now = NSDate()
    
    timeLabel.text = dateFormatter.stringFromDate(now);
        
    delay(1.0, closure: { () -> () in         
         self.updateLabel()       
    })
}
    
func delay(delay:Double, closure:()->()) {
   dispatch_after(
       dispatch_time(
            DISPATCH_TIME_NOW,
            Int64(delay * Double(NSEC_PER_SEC))
        ),
        dispatch_get_main_queue(), closure)
}

Because we are using closures we need to think whether we’re creating a strong cycle reference or not. If we just call self.updateLabel() then we will create a strong reference cycle. In our case it doesn’t really matter, the view controller will be around for the whole duration of the application’s life cycle. But since we’re doing this to learn how to use GCD, let’s learn the proper way to do it. In Objective C all we would need to do is create a weak self reference and we’d be good to go. In Swift, we have two options for a weak reference: weak and unowned. Use weak when it is possible for that value to have “no value” at some point in its life. An unowned reference is assumed to always have a value. An unowned reference is a non-optional type, while the weak reference is an optional type in Swift. Read more about it in Apple’s book.

If we’re using the weak reference then we also need to make sure that we unwrap the value:

delay(1.0, closure: { [weak self] () -> () in
            
   if let strongSelf = self {
        strongSelf.updateLabel()    
   }
})

If we use the unowned value then we don’t need to unwrap it:

delay(1.0, closure: { [unowned self] () -> () in
            
    self.updateLabel()
})
Any of the two will work in our case, but read the Apple docs and know when to use one or the other.
I hope you enjoyed today’s challenge. It goes to show that even 5 minutes of coding can teach you some important lessons. It also shows that the same problem can be solved in many different ways and that is one of the reasons why I love programming. Do you have another way to solve the challenge? I’d love to hear about it, so leave a comment!

One Reply to “Swift Fun with Timers and GCD”

Leave a Reply

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

*