Hey guys let’s make a new app. We’re going to talk a bit about threads and stuff here. For those that don’t know, threads are… blah blah blah.

Let’s make an app called Carnival Checker. A CS major named Matt Schallert made a website called http://www.isitcarnivalyet.com and it just displays YES or NO. We’re going to make an app that access that website and tells us.

CarnivalChecker.Swift

//
//  CarnivalChecker.swift
//  CARNIVAL
//
//  Created by Michael Gazzola on 11/24/14.
//  Copyright (c) 2014 Michael Gazzola. All rights reserved.
//

import Foundation

let urlString: String = "http://www.isitcarnivalyet.com"
let url: NSURL = NSURL(string: urlString)!

class CarnivalChecker: NSObject {
    
    class func carnivalYet() -> String {
        sleep(3)
        let request = NSURLRequest(URL: url)
        let response: AutoreleasingUnsafeMutablePointer<NSURLResponse?> = nil
        let data: NSData = NSURLConnection.sendSynchronousRequest(request, returningResponse: response, error: nil)!
        let dataString = String(NSString(data: data, encoding: NSUTF8StringEncoding)!)
        if ((dataString.rangeOfString("NO")) != nil) {
            return "NO"
        } else {
            return "YES"
        }
    }
}

Okay great. Notice that we chose to make a synchronous request. Normally we would make an async request, but we won’t so we can demonstrate how to use threads here. Sync just means that the function will return as soon as it hears back from the webpage.

Let’s make our app’s main page. Label on top, label in the middle, button on the bottom. Simple. Okay, and it works really easy. Now, let’s simulate latency (add sleep(3) to CarnivalChecker). Okay, now the app doesn’t work so well. How can we fix? We’ll add an activity indicator!

Oh now, the activity indicator freezes. Actually, everything freezes! This isn’t good! Real apps don’t do this! This sucks. How do we fix this. We’ll use threads.

In general, UI stuff runs on the main thread, since that’s the most important stuff. So, we’ll need to run a secondary thread to gather the website information for us and update the label. On the main thread, we’ll start the spinner.

    @IBAction func checkCarnival(sender: UIButton) {
        self.activityIndicator.startAnimating()
        self.answerLabel.text = "CHECKING..."
        dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0), { () -> Void in
            self.answerLabel.text = CarnivalChecker.carnivalYet()
            self.activityIndicator.stopAnimating()
        })
    }

We can have some extra lines for hiding and showing the activity indicator. Now, it’s hard to see because this is such a basic example, but this is still wrong. Remember, the main queue is saved for UI updates, and we’re updating the UI on a background queue. This violates a concept known as thread affinity, which says that you should do work on objects—creating, modifying, destroying—on the same thread. This, in general, makes things faster.

It’s hard to see here, but things aren’t running optimally by doing UI updates on a background thread. Therefore, we need to punt the UI updates back over the main thread We can do that like this:

    @IBAction func checkCarnival(sender: UIButton) {
        self.activityIndicator.hidden = false
        self.activityIndicator.startAnimating()
        self.answerLabel.text = "CHECKING..."
        dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0), { () -> Void in
            let text = CarnivalChecker.carnivalYet()
            dispatch_async(dispatch_get_main_queue(), { () -> Void in
                self.answerLabel.text = text
                self.activityIndicator.stopAnimating()
                self.activityIndicator.hidden = true
            })
        })
    }

Now, lets talk about QOS classes. You basically have these global queues that your block can jump on. But it’s that block’s turn in the queue, it gets its run time. However, some queues have different priority over others. If we command click on QOS_CLASS_USER_INITIATED, we can see the different priorities of threads.

And now the app works reasonably well!

Unfortunately, if our user is a monkey, they could spend all day smashing on the button and making TONS of requests! This is bad. We might want to bar requests that are happening while we are checking on things. We can naively do that like this

    @IBAction func checkCarnival(sender: UIButton) {
        self.activityIndicator.hidden = false
        self.activityIndicator.startAnimating()
        self.answerLabel.text = "CHECKING..."
        if (!self.checking) {
            self.checking = true
            dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0), { () -> Void in
                println("HIT")
                let text = CarnivalChecker.carnivalYet()
                dispatch_async(dispatch_get_main_queue(), { () -> Void in
                    self.answerLabel.text = text
                    self.activityIndicator.stopAnimating()
                    self.activityIndicator.hidden = true
                    self.checking = false
                })
            })
        }
    }

Is it true here that ONLY ONE REQUEST can be happening at a time? No. This is a potential TOCTTOU problem. Albeit, a minor one that could never really affect our app, but it exists. If two button presses happen really quickly, they could both quickly enter the if statements and make two requests. This isn’t a problem for an app this simple but if carnivalYet() were an extremely heavy operation, you could see the issue here.

There are synchronization fixes for this, but we won’t cover them today. In general, you aren’t doing *that much* multithreading outside of the Apple provided async stuff, but what we covered today should talk about most of your needs.

Now, let’s make a Today widget for this. Go to File > New > Target. Make a Today widget. You’ll see a TodayViewController and a MainInterface.storyboard!