Closures are a very useful part of Swift. An Int is a type that stores an Integer, a String is a type that holds a string. In the same way, a closure is, basically, a type that holds a function. With this capability, you can do many things with closures that you couldn’t do in several older languages. You can assign closures to a variable, you can pass them as arguments to other functions, you can even return one from a function.
Closures in Swift are similar to blocks in Objective-C. In fact, when you call an Objective-C API from Swift that wants a block, you pass in a closure. The major difference between them is coding style, and that closure expressions are much easier to read and write in Swift.
Functions are Closures?
You may not have realized it, but we have already been using closures in Swift. Functions in Swift are actually a certain type of closure. I’ll let Apple’s iBook speak for itself here:
Closures take one of three forms:
- Global functions are closures that have a name and do not capture any values.
- Nested functions are closures that have a name and can capture values from their enclosing function.
- Closure expressions are unnamed closures written in a lightweight syntax that can capture values from their surrounding context.
Excerpt From: Apple Inc. “The Swift Programming Language.” iBooks. https://itun.es/us/jEUH0.l
Today, we are going to discuss a bit about the third, particularly how to remove a lot of the fluff and compress them down into a very readable, but concise form.
Remember the array’s map method that was mentioned in my previous post Arrays and their Methods in Swift? The map method takes a closure as an argument, performs that closure on every element of the array, and returns back a new array of what is returned. This new array doesn’t even have to be of the same type. Let us say we wanted to greet everybody on an array called guestList. We could generate strings for greeting them like so:
func greetPeople(person: String) -> String { return "Hello, \(person)!" } let guestList = ["Chris", "Jill", "Tim"] let fullGreetings = guestList.map(greetPeople)
We made a greetPeople function, and then passed that into the map method for our guestList array. It then went through, generated the full greeted Strings, and then returned them to us, where we stored them in fullGreetings.
Closure Expression Compression
This could be made more succinct using some of the shortcuts available to us from Swift. If we only really need to do this greeting once, we don’t really need a function for it. Naming the function lets us call it anywhere, and we simply don’t need that capability, we only want to call this once, and we can cache those returned strings for later, if need be.
This is where we can use closure expressions. I particularly like Airspeedvelocity’s explanation of Closure Expressions. Closure expressions, are basically function “literals.” The more official title for them are anonymous functions. When you just need a quick function stated in only one place (like in a completion handler), you don’t need to give it a name, and just pass in the code for the function directly. Some other programming languages refer to them as Lambda expressions.
Here is the most complete and explicit form of our code above used as a closure expression:
let fullGreetings = guestList.map({(person: String) -> String in return "Hello, \(person)!"})
It might look a little messy, but it is still better than having that whole separate function written out when you would only use it once anyway. In its fullest form, the syntax for a closure expression is a normal function prototype (minus the func keyword), followed by in, and then the actual body of the closure expression. The in basically tells it that the prototype is complete, and the rest is what the closure expression actually does.
Below I am going to quickly step through the various ways to make this Swift closure expression even more succinct.
First of all, this is Swift! We have type inference now, let us get rid of those types from the parameters:
let fullGreetings = guestList.map({(person) -> String in return "Hello, \(person)!" })
Actually, the compiler can see that we are returning a string (since we do say ‘return “Some String” ‘in the closure), so we don’t even need the return type in there either, so let’s take that out:
let fullGreetings = guestList.map({person in return "Hello, \(person)!"})
For single expression closures, like ours, the only expression in the closure body is a string literal we are creating, so we don’t even need the “return” keyword there either, so we’ll get rid of that next:
let fullGreetings = guestList.map({person in "Hello, \(person)!" })
Actually, Swift has shorthand names for the parameters to the closure expression in the form of $0, $1, $2, and so on. With that, we don’t even need to state the names of the parameters, or the in keyword either:
let fullGreetings = guestList.map({ "Hello, \($0)!" })
While not as useful here, since this is all on one line, in larger closure expressions, we can make this a bit more readable with trailing closure syntax. It lets you put the curly braces on the outside, to look more like a normal function:
let fullGreetings = guestList.map(){ "Hello, \($0)!" }
Finally, since we don’t have anything else in the parenthesis for the map method, we can actually just remove them altogether:
let fullGreetings = guestList.map{ "Hello, \($0)!" }
Now that is a compact closure expression, and makes what it does very obvious.
Conclusion
This is only the beginning of our discussions about closures and closure expressions. There is more to come, especially about writing your own functions that take closures. Check out my next post Closures and Capturing Values in Swift to learn how to do that.
I hope you found this article helpful. If you did, please don’t hesitate to share this post on Twitter or your social media of choice, every share helps. Of course, if you have any questions, don’t hesitate to contact me on the Contact Page, or on Twitter @CodingExplorer, and I’ll see what I can do. Thanks!