Now let’s write our own closure!
func fetchMostRecentDataOfQuantityType(quantityType: HKQuantityType, withCompletion completion: ((mostRecentQuantity:HKQuantity?, error:NSError?) -> ())? ) {
let timeSortDescriptor = NSSortDescriptor(key: HKSampleSortIdentifierEndDate, ascending: false)
[timeSortDescriptor], resultsHandler: { (query:HKSampleQuery!, results:[AnyObject]!, error:NSError?) -> Void in
let query = HKSampleQuery(sampleType: quantityType, predicate: nil, limit: 1, sortDescriptors: [timeSortDescriptor]) { query, results, error in
if completion != nil && error != nil {
completion!(mostRecentQuantity: nil, error: error)
return;
}
let resultsArray = results as NSArray?
var quantitySample: HKQuantitySample? = resultsArray?.firstObject as HKQuantitySample?
var quantity: HKQuantity? = quantitySample?.quantity
if completion != nil {
completion!(mostRecentQuantity: quantity, error: error)
}
}
self.healthStore?.executeQuery(query)
}
Notice we declare a function that takes a quantityType parameter and a completion parameter. The completion parameter is a closure (it also has an internal and external name). The closure is defined as:
{ (mostRecentQuantity, error) -> () } // which takes 2 parameters itself and returns void *dont worry about the ?
Internally, the function uses the completion block. Whenever we wish to call the completion block, we test for it and say completion (mostRecentQuantity, error) which sets the completion block. Again don’t worry about the ?
Now let’s actually use that function in code. We call it with self and pass it in heightType as the quantityType parameter and pass it the values for mostRecentQuantity and any error and the code to be executed. That code takes the error and checks if there is any in order to log it. Otherwise, it takes mostRecentQuantity and uses it to set a local variable.
self.fetchMostRecentDataOfQuantityType(heightType, withCompletion: { (mostRecentQuantity:HKQuantity?, error:NSError?) -> () in
if let something = error {
NSLog(“An error occured fetching the user’s height information. In your app, try to handle this gracefully. The error was: %@.”, something)
abort()
}
//Determine the height in the required unit.
var usersHeight: Double = 0.0
if let somethingElse = mostRecentQuantity {
var heightUnit: HKUnit = HKUnit.inchUnit()
usersHeight = mostRecentQuantity!.doubleValueForUnit(heightUnit)
// Update the user interface.
dispatch_async(dispatch_get_main_queue(), {
self.heightValueTextField.text = NSNumberFormatter.localizedStringFromNumber(usersHeight, numberStyle:.NoStyle)
})
}
})
Here is another example of one we can write. I actually borrowed this from a web tutorial and a GitHub post:
func searchUsersWithSearchTerm(searchTerm: String, completionClosure: (users :User[]) ->()) {
var request = NSMutableURLRequest(URL: NSURL(string: "https://api.github.com/search/users?q=\(searchTerm)"))
request.HTTPMethod = "GET"
let postDataTask = self.urlSession.dataTaskWithRequest(request, completionHandler: {(data, response, error) in
if error {
println(error.localizedDescription)
}
println(response)
var repoJSON : NSMutableDictionary = NSJSONSerialization.JSONObjectWithData(data, options:NSJSONReadingOptions.MutableContainers, error: nil) as NSMutableDictionary
var jsonArray = repoJSON["items"] as NSMutableArray
var users : User[] = User().parseJSONIntoUsers(jsonArray)
println(users.count)
completionClosure(users: users)
})
postDataTask.resume()
}
The function takes a searchTerm parameter and a completionClosure much like the previous one. That closure is:
{ (user:[User]) -> ( ) } // takes a User array named user and returns void.
The function performs some heavy-slow work and then ends up populating an array of Users. Once we have our data, we want to call our completion block. So normally you would use a parameter inside a function to do something. Here we are using our closure parameter to do something, call it! We call it by saying closure(parameters) as if we were calling a function(). To call a function you pass in the parameters that function requires. To call a closure you call the parameters that closure requires. In this case, our closure only requires one parameter, the users array. So basically the code inside the function will be executed serially and only after the data is returned from the fetch, will the completion closure be called. Once its called, then it will do whatever was passed into it inside { }.
Now how would we call this function:
self.someController.searchUsers("Woz") { (users: User[]) in
self.searchUsers = users
NSOperationQueue.mainQueue().addOperationWithBlock() { () in
self.collectionView.reloadData()
}
}
We call the function by passing it the two parameters it requires (searchTerm and completion closure). Normally we would say (param1,param2), but when the last parameter of a function is a closure, we are allowed to tag it as:
function(param1) { //closure code }
In short, when you call a function that has a completion closure as a parameter you pass it the parameters it needs to execute it’s own code and the closure to execute afterwards. It first executes it’s own code (which in our case got data from the web) and then that same code is in charge of calling a completion closure only after it’s done with it’s own code. By calling completion(), the function is calling the completion closure which contains the code we passed it.
How to properly use pre-made closures:
1. var = method(param1, param2, param3) handler4{ params in //if code } //handler4 is optional
OR
2. var = method(param1, param2, param3, handler:{() -> Void in //if code })
Hope you enjoyed this.