Goodbye blocks: Passing a closure to a parameter in Swift (Xcode/iOS)


Problem

Sooner or later you'll need to pass a closure to a parameter in Swift when using a Cocoa Touch framework. So let's do something simple to illustrate how this is done by looking at the method animateWithDuration, which has a declaration like this:

class func animateWithDuration(duration: NSTimeInterval, animations: (() -> Void)!, completion: ((Bool) -> Void)!)

So here we see that the animations parameter requires a closure that receives no parameters and returns Void, while the completion parameter must accept a Bool and return void.

Solution(s)

UIView.animateWithDuration(2.0, animations: {()->Void in self.view.backgroundColor=UIColor.brownColor()}, completion: {(finished:Bool)->Void in self.view.backgroundColor=UIColor.yellowColor()})

The line of code that I've written above satisfies this because the animations closure has no parameters and returns Void, while the completion closure takes a parameter named finished, which is of type Bool, and again returns Void.

Now as with any method, we don't need to explicity write Void when something returns Void, and in this case neither do we need to write () when a closure has no parameters to be passed values to, so we can remove all of this stuff.

UIView.animateWithDuration(2.0, animations: {self.view.backgroundColor=UIColor.brownColor()}, completion: {(finished:Bool) in self.view.backgroundColor=UIColor.yellowColor()})

And while we're stripping back to the bare essentials, we don't need to supply the type, and if we're not supplying the type then neither do we need the parentheses around finished in the completion closure, which means we can now write this:

UIView.animateWithDuration(2.0, animations: {self.view.backgroundColor=UIColor.brownColor()}, completion: {finished in self.view.backgroundColor=UIColor.yellowColor(); println("Really finished? \(finished)")})

Note: I've added the code println("Really finished? \(finished)") to illustrate that we also have access to the value passed to our parameter. This is true throughout and the code I've just added could equally be included in any of the closures.

Explanation

Every single one of these closures is correct and does exactly the same thing, they are just increasingly stripped back, and depending on your viewpoint each step adds or subtracts from the readability of your code.

Acknowledgments

Thanks go to macbeta and mascot for help with this code.

Note

The term closure in Swift is treated in a more general way than found elsewhere:

Closures are self-contained blocks of functionality that can be passed around and used in your code. Closures in Swift are similar to blocks in C and Objective-C and to lambdas in other programming languages. Apple Developer Site

and this is something that I hope to explore in greater detail in a later post.

Endorse on Coderwall

Comments