Let’s analyze some useful applications of closures in everyday code!
Ok so let’s take a look at a real life function that uses a closure. A typical closure is a completion handler. A completion handler is a parameter, just like any other, that is passed into a function as a closure. When that code gets executed, the completion handler gets filled. Then your function will use the value of that completion handler to do something useful.
Here are some typical uses of closures in API’s. The typical one is UIView.animateWithDuration. Open Xcode and start writing UIView.anim… and it will autocomplete for you with this:
UIView.animateWithDuration(<duration: NSTimeInterval>, animations: <() -> Void() -> Void>)
This is telling you that the animateWithDuration takes 2 parameters:
duration which is an NSInterval type called duration &
animations which is a closure type which takes void parameters and returns void. Remember, animations is just another variable we are passing in, so its separated with a comma from duration.
If you hit enter over the duration parameter, you can write in whatever you want. If you then tab, it moves over to the ()->Void parameter. Hit enter again. The closure turns into this format:
{ () -> Void in
<#code#>
}
Where the { } define your closure and you can see the closure takes () and returns -> () as well. Except that in this case the final () after -> are replaced by the keyword Void. Then we get our keyword to transition from the ending ->() in a function to our actual function or closure code which will be enclosed in { }.
In the end you might end up with something like this:
UIView.animateWithDuration(1,
animations: {() in redBox.alpha = 0 },
completion:{(Bool) in println("red box has faded out") })
which is interpreted as:
The function animateWithDuration says: “The value for the duration parameter is 1 second. The value for animation is a closure of block of code enclosed by { }. Finally the value for completion is also a closure enclosed by a second { }.”
The animation closure says: “I take no parameters and return void and my code is = in = change the redBox’s alpha to 0.
The completion closure says: “I takes a Bool parameter and returns void and my code is = in = print the message.”
So we are passing those 2 blocks of code to be executed into the function as parameters.
It basically translates to this:
UIView.animateWithDuration(duration: NSTimeInterval,
animations: (() -> Void)?,
completion: ((Bool) -> Void)?)
Where are you can see, 3 parameters out of which 2 are closures. Both closures return void but the second one takes a Bool.
So what does it mean? Well, it says:
1. Here is how much the duration is going to last
2. Here is the animation you will perform inside of the { }. That requires no parameters and returns nothing.
3. Oh and btw, here is another block of code { } but take a Bool and only execute { } if the Bool is true. The Bool is set to true when the animation passed in has already finished.
Let’s take a quick look at some closures you’ll find in Apple API’s:
1. This passes in the dispatch queue as parameter 1 and a closure to downloadData as parameter 2.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), {
self.downloadDataForRegisteredObjects(true, toDelete:false)
})
2. This function called performBlockAndWait passes in a closure without parameters and with Void and the code to be executed PLUS another function called executeFetchRequest.
CoreDataController.sharedDataController.backgroundManagedObjectContext?.performBlockAndWait({ () -> Void in
var error:NSError?
var results:NSArray
CoreDataController.sharedDataController.backgroundManagedObjectContext?.executeFetchRequest(request, error:&error)
if ((results.lastObject) != nil) {
date = (results.lastObject as NSArray).valueForKey("updatedAt") as? NSDate
}
})
3. This assigns a function return to a variable. That function is GET() and it receives a className parameter, a parameters parameter and a success completion block parameter. That success completion block parameter is a closure that takes an operation and a response and returns void. It is called when the operation was successful and writes the operation’s response to disk. If the operation fails, it logs the error.
var operation: AFHTTPRequestOperation = self.GET("classes/\(className)", parameters: parameters, success: { (operation, response) -> Void in
// success code...
if response.isKindOfClass(NSDictionary) {
self.writeJSONResponse(response, className:className as NSString)
}
}, failure: { (operation, error) -> Void in
// failure code...
NSLog("Request for class %@ failed with error: \(className), \(error)")
})
4. This assigns the function result to a variable as well. It calls the batchOfRequestOperations method and passes it an operationsArray and a progressBlock and a completion block but in this case you can’t see the “completion” name of that last parameter. Sometimes when you start writing out a function name and it takes a closure as a parameter and its the last one, you don’t have to name it? Why?
var batchOperations: NSArray = AFURLConnectionOperation.batchOfRequestOperations(operationsArray,
progressBlock: { (finishedOps:UInt, totalOps:UInt) -> Void in
NSLog("%lu of %lu complete", finishedOps, totalOps)
}) { (op) -> Void in
if (!toDelete) {
self.processJSONDataRecordsIntoCoreData()
} else {
self.processJSONDataRecordsForDeletion()
}
}
I guess its because it’s the last parameter in the method call.
Next we take a look at writing our own closures.