Writing Completion Blocks with Closures in Swift

I never fully understand blocks until I learned how to write a method that took a block in as a parameter. Even after I had a good handle on the concept of blocks, I still had to look up their syntax every time I wanted to write anything but the simplest of blocks (thank you fuckingblocksyntax.com). Thankfully, Swift’s version of blocks, known as closures, take a much cleaner, more natural approach to syntax. So without further ado, time to learn closures the way my Dad taught me, by writing a completion block.

One of the most common area’s iOS devs use blocks is with UIView’s animation API. Before blindly writing your own method, try using one of Apple’s code samples to familiarize yourself with the syntax.  Below is an Obj-C example, and then the same code written in swift:

    //setup a simple red view
UIView *redBox = [[UIView alloc] initWithFrame:CGRectMake(0,    0, 100, 100)];
    redBox.backgroundColor = [UIColor redColor];
    [self.view addSubview:redBox];
    [UIView animateWithDuration:1 animations:^{
        redBox.alpha = 0;
    } completion:^(BOOL finished) {
        NSLog(@"Red box has faded out");
    }];

And now for the swift version:

let redBox = UIView(frame: CGRectMake(0, 0, 100, 100))
    redBox.backgroundColor = UIColor.redColor()
    self.view.addSubview(redBox)
    UIView.animateWithDuration(1, animations:  {() in
       redBox.alpha = 0
      }, completion:{(Bool)  in
        println("red box has faded out")
      })

Much to the dismay of rabbits and near-sighted developers, carrots ( ^ ) are nowhere to be found (yet) in Swift. Rest in peace old friend, you have been replaced by the keyword 'in'.  You use the keyword 'in' once you have finished declaring the parameters and the return values for your closure. This will make more sense if I show you what autocomplete filled in once it knew what method I wanted:

UIView.animateWithDuration(duration: NSTimeInterval, 
                         animations: (() -> Void)?, 
                         completion: ((Bool) -> Void)?)

Focusing on this syntax, it becomes clear how to properly write the parameters and return values of a closure: 

{(parameters) -> (return type) in expression statements}

In the Swift example code above, both of the animation closures returned void, so we could drop the return type syntax all together. Thats why you only see the ( ) for the parameters. Notice that even if there is no parameters, like in the first closure, you still need those empty ( ) before the keyword in. They are required. Bolded so you learn it, learn it good.

Okay, now let's try writing our own Swift method with a completion closure. In the Github repository linked at the bottom of this post, I have been working on a Github client app in Swift with a team of Code Fellows. In this project we have a network controller that handles all of the asynchronous network calls to Github's API.

Let's write a method that retrieves a list of users from a search query on the client. This method will need a completion closure to let the requesting View Controller know when the network call is finished, and provide an array of User models to populate our collection view. 

Heres what the signature of the function would look like:

func searchUsersWithSearchTerm(searchTerm: String, 
                        completionClosure: (users :User[]) ->()) 

We've got 2 parameters here, a string type called searchTerm, and then our completion closure.  Notice the syntax in the closure parameter?  First is the name of our closure, which is how we will call it later in the method, then the parameter (array of Users), followed by our return value (void). Below is an approximation of what the body of this method looks like:

//setup a NSMutableRequest with the correct API URL and parameters
//setup a dataTask on our NSURLSession instance
//once our datatask has successfully retrieved the data we wanted,  and we have parsed through it to create an array of User Models, we call our completion closure like this:
var users : User[] = //Parse the JSON data into Users
competionClosure(users: users)

Calling our completion closure is nearly the exact same way calling our completion block would be in Objective-C. It's called just like any other function, and we make sure to pass in an array as a parameter, since thats how we defined up in our function signature. I purposely left out all the networking code to keep this blog post as short as I could, but feel free to check out the Github repository if you are interested in seeing how it works. We will have a blog post up on network in Swift in the near future.

Finally, lets take a look at how we would call this method from our SearchUsersViewController:

self.networkController!.searchUsers("Dr. Dre") { (users: User[]) in
    self.searchUsers = users
    NSOperationQueue.mainQueue().addOperationWithBlock() { () in
        self.collectionView.reloadData()
    }
}

One more thing I want to you notice is how I called addOperationWIthBlock() on our mainQueue(). If a closure is the last parameter of a function's signature, you can use special syntax to make it even more clean. This is referred to as a Trailing Closure. Trailing Closures are written directly after the ( )'s of a function call. Below is a function call, first with a regular closure, then with a trailing closure.

//regular closure
doSomethingAwesome({ (isAwesome : Bool) in
  //inside our closure
})
//trailing closure
doSomethingAwesomer(){ (isAwesomer : Bool) in
  //inside our closure
}

It's a subtle difference, and both ways work. Just remember it has to be the last parameter of the function in order to use the trailing closure syntax.

Now you should know the basic syntax of Closures in Swift. For some more advanced level stuff on Closures, check out the Swift Programming Language on iBooks, it has an entire subsection on them. And here's the link to the github client project.

There's also already a Swift counterpart to fuckingblocksyntax.com called, you guessed it, fuckingclosuresyntax.com that's definitely worth adding to your bookmarks bar as you learn Swift. 

Good Day.