Closures, what a great tool! Once you get it, you love it. Closures are the famous blocks in Objective-C. Maybe were a bit difficult to understand at first sight. I’ve written three parts in this blog to explain them. However now everything is easier. Closures are vital part in Swift and we will experiment with them to learn it.
Closures are like functions without names. So they look like:
{ (item1: type, item2: type) -> type in //code }
The closure is defined inside curly braces { }. Inside the parenthesis we have the list of parameters. Then the arrow “->” points to the return type and after that the keyword in separates the declarations from the implementation code.
Simple example. Lets declare a variable that can hold the closure.
var aClosure: (Int) -> String
This is a closure that takes one argument of type Int and returns a String. Time to write the closure now and assign it to the variable:
aClosure = { (times: Int) -> String in return "I called you \(times)" }
Easy and how we can call it? Like that:
aClosure(5)
Some of you may think…”Hmm, you told us that the closure has no name but you use a name by assign it to a variable, cheat!”
OK, we can use the closure in place by using the parenthesis () just as a function. Take a look in the following example:
let greeting = "Hello, " + { (times: Int) -> String in return "I called you \(times) times" }(5)
The greeting string will be: “Hello, I called you 5 times”
A closure offers a concise way to write code, a swift way 🙂
To realise this I have to present the following example. After that closures in swift will be your friend (I don’t know if it will the best, we have more 101 guides, we’ll find out later!)
Lets write a closure that calculates the volume of a cylinder and call it.
import Darwin // to get access to M_PI var volume1 = { (height: Double, radius: Double) -> Double in return M_PI * pow(radius, 2) * height } let cylinder1 = volume1(10, 10)
Everything is as I have already described you. It’s time for diet though. Since the closure has only one command the return keyword can be omitted. So the following will work fine as well
var volume2 = { (height: Double, radius: Double) -> Double in M_PI * pow(radius, 2) * height } let cylinder2 = volume2(10, 10)
If we have more commands there return is required. Swift supports type inference. The product of a multiplication of double numbers is also a double and swift can perfectly understand it. So the following will work as well.
var volume3 = { (height: Double, radius: Double) in M_PI * pow(radius, 2) * height } let cylinder3 = volume3(10, 10)
Lets continue. If we have previously declare the type of the closure the reduction can be even more. Either way, consider that this is usually the case since you will requested to write closures as arguments in methods where the types are predefined. An example please!
var volume4: (Double, Double) -> Double volume4 = { (height, radius) in M_PI * pow(radius, 2) * height } let cylinder4 = volume4(10, 10)
Everything is in place the implementation is easy. However, we can go even deeper. The arguments can be replaced with defaults of the form $n where n in the order in the list sequence. Eventually our closure can become like:
var volume5: (Double, Double) -> Double volume5 = { M_PI * pow($0, 2) * $1 } let cylinder5 = volume5(10, 10)
Well maybe the example is not representative since we know beforehand which is the radius and the heigh although this is in not clear in the declaration, but for the shake of the explanation could be accepted. Nevertheless this type of default parameter list is work perfect for simple cases.
Another example that can illustrate how the closures can facilitate coding. In this case we are going to build a simple calculator:
func calculator(a: Int, b: Int, withOperator op: (Int, Int) -> Int) -> Int
It takes two operands and an operator in the form of closure. So, the operators will look like:
let add: (Int, Int) -> Int = { $0 + $1 } let sub: (Int, Int) -> Int = { $0 - $1 } let multi: (Int, Int) -> Int = { $0 * $1 }
Nothing new, although you can see the power of closures. Short code, less bugs.
Now lets implement the calculator function.
func calculator(a: Int, b: Int, withOperator op: (Int, Int) -> Int) -> Int { return op(a, b) }
Simple as that. Few tests will help us to understand how it works:
calculator(10, b: 10, withOperator: add) // 20 calculator(10, b: 5, withOperator: sub) // 5 calculator(10, b: 10, withOperator: multi) // 100
Another option, which is called trailing closure syntax, is to move out the closure from the arguments’ list if the closure is the last parameter. Explain please…
let result = calculator(10, b: 5) { $0 / $1}
instead of
let result = calculator(10, b: 5, withOperator: { $0 / $1})
There is another important concept related with closures. The name of them comes from the fact that captures the constants and the variables from the surrounding context in which it is defined. So instead of having access only within their scope between the curly braces, they can capture the state of the scope that includes them. In addition it reserves the value of them even if the scope no longer exists when the closure is executed.
So an example could be the following
var radius = 10.0 let myClosure: () -> Double = { radius++ return M_PI * radius * radius } myClosure() // 380.13 calculated with radius equal to 11 radius // 11
Exactly! The closure has access to radius variable even this is defined outside. A great example about that can be found in Swift’s official documentation here.
This was a introduction about closures. Experiment and learn!
You can find the related playground file here.