Closures in Swift

It took me some time to write a new post… and that is not because I didn’t had any topics where I would like to write about, but it was mainly because I got stuck in too many new (great) things to work on. Today I’d like to start with a new small topic though which hurt my brains for years and that is “Closures”.

Basic Closures

In Swift we can use functions just like any other type, such as an Integer, Double or String. So that means that we can assign a function to a variable and then run that function through this variable. Functions can even be passed into other functions this way. When you use a function in this way it is called a closure.

func soccerTeam() {
  print("PSV is the best soccer team")
}

soccerTeam()

In the example above we created a regular function which simply print some text when called. Below we created the same example but then used a closure to show the text. The end result is the same in both examples.

let soccerTeam = {
  print("PSV is the best soccer team")
}

soccerTeam()

As you might have noticed in the way the closure is written there is no space to have it accept parameters but of course they do accept them but you just write it differently. You can simply list the parameters inside the parentheses just after the opening brace.

let soccerTeam = { (team: String) in 
  print("\(team) is the best soccer team")
}

soccerTeam("PSV")
One difference between closures and functions is that when you call a closure you do not use parameter labels (e.g. 'team')

Besides accepting values, a closure can also return values. You write them in the closure just before the ‘in’ keyword.

let soccerTeam = { (team: String) -> String in 
  return "\(team) is the best soccer team"
}

let soccerInfo = soccerTeam("PSV")
print(soccerInfo)

This way we print the same String on the console, however, this time the closure itself just returns the string and we decide to print it again but we could also have done something else with this String.

Use closures as parameters

So far so good, now let’s make it a little bit more complicated :-). We mentioned at the start of this post that you can use functions just like any other object type (like integer or string). So that does mean that we can even use closures as arguments to pass into functions.

If you pass a closure that does not accept parameters and does not return anything into a function (so that this closure could be used inside that function) you specify the type as ‘() -> Void’. This simply means that the closure accepts no parameters ‘()’ and returns nothing ‘Void’.

let soccerTeam = {
  print("PSV is the best soccer team")
}

func soccerGala(announcement: () -> Void) {
  print("Let's start with the team prices")
  announcement()
  print("But that is no surprise")
}

soccerGala(announcement: soccerTeam)

So in the example above we create a closure with the name ‘soccerTeam’ and besides that we also create a function with the name ‘soccerGala’. This function accepts one argument with the label ‘announcement’ and the type of this object is a closure which accepts and returns no parameters ‘() -> Void’. If this function accepted a argument of type String then you would have written it as follows:

func soccerGala(announcement: String) {
  ...
}

So instead of a type like “String” you use the type “() -> Void” for the announcement argument.

Now we can call our function with the command ‘soccerGala(announcement: soccerTeam)’. This means that our soccerTeam closure gets passed into our function which results in the following text on the console:

Let’s start with the team prices <– print statement from our function
PSV is the best soccer team <– print statement from our closure
But that is no surprise <– print statement from our function

Closures with parameters

A Closure which you pass into a function can however also accept its own parameters and that is where reading closures gets a bit tricky to understand.

When you use ‘() -> Void’ like in our previous examples it means that the closures does not accept parameters and returns nothing, but you can simply add any parameters your closure should accept inside the ‘()’.

let soccerTeam = { (team: String) in
  print("\(team) is the best soccer team")
}

func soccerGala(clubName: String, announcement: (String) -> Void) {
  print("Let's start with the team prices")
  announcement(clubName)
  print("But that is no surprise")
}

soccerGala(clubName: "PSV", announcement: soccerTeam)

In the example above our ‘soccerTeam’ closure accepts a String (the team name) as a parameter and still returns nothing. Besides that the function itself also accepts an argument of type String with the label ‘clubName’.

So when we use our closure inside our function, the type of announcement changes into ‘(String) -> Void’. This means that we must call the function providing a closure which accepts one parameter as String. The end result is the same as before, the only difference is that “PSV” is now passed as an argument from the function into the closure.

Closures which return values

The final option which we did not discuss yet is when a closure returns a value. So far we used ‘-> Void’ to indicate that our closure does not return values. However, when the closure is supposed to return a value you can replace Void with the actual object type.

let soccerTeam = { (team: String) -> String in
  return "\(team) is the best soccer team"
}

func soccerGala(clubName: String, announcement: (String) -> String) {
  print("Let's start with the team prices")
  announcement(clubName)
  let say = announcement(clubName)
  print(say)
  print("But that is no surprise")
}

soccerGala(clubName: "PSV", announcement: soccerTeam)

So as you can see, there is not much different compared to our last example. The only difference is that our closure ‘soccerTeam’ now accepts a String ánd returns a String. So in our function we now get the text ‘PSV is the best soccer team’ and assign it to the variable ‘say’ and then we print this line from the function itself.

Use closures in function calls

So far I have been defining the closure in a variable (‘soccerTeam’) and assign it to the function call by using ‘soccerGala(clubName: “PSV”, announcement: soccerTeam)’. However, we can also write the closure as an argument to the function call like this:

func soccerGala(announcement: () -> Void) {
  print("Let's start with the team prices")
  announcement()
  print("But that is no surprise")
}

soccerGala(announcement: {
    print("PSV is the best soccer team")
})

When the last parameter to a function is a closure you also use a special syntax called ‘trailing closure’. This means that instead of passing in the closure as a parameter of the function, you can pass it in directly after the function inside braces.

soccerGala() {
    print("PSV is the best soccer team")
}

Since there are in our example above no other parameters beside our closure we can even omit the function parentheses completely like this:

soccerGala {
    print("PSV is the best soccer team")
}

Rather than pass in your closure as a parameter, you pass it directly after the function inside braces. Trailing Closures are used a lot inside Swift so it is really important to understand this syntax.

Shorthand parameters

Let’s go back to our soccerGala function which accepts a closure as parameter which itself also accepts one parameter and returns a string.
Then we can call our function through the line below it (as a trailing closure syntax).

func soccerGala(announcement: (String) -> String) {
  print("Let's start with the team prices")
  let say = announcement("PSV")
  print(say)
  print("But that is no surprise")
}

soccerGala { (team: String) -> String in
    return "\(team) is the best soccer team"
}

However, Swift already knows from the function declaration that the parameter to the closure should be a String and also that the return value of the closure should be a String. So we can omit these values from the function call which reduces the amount of code a bit.

soccerGala { team in 
    return "\(team) is the best soccer team"
}

But we can make our code even smaller, since our closure only has one line of code it must be the one which returns the value, therefore Swift allows us to remove the return keyword too.

soccerGala { team in 
   "\(team) is the best soccer team"
}

Finally Swift also has a shorthand syntax that allows us to make the code even shorter. We can replace the ‘team in’ part with automatic names for the closure’s parameters. These names start with a ‘$’ sign and then a number counting from 0 upwards.

soccerGala { "\($0) is the best soccer team" }

So what about using multiple input parameter for our closure? E.g. the club name and the position they ended on? Then we can simply count upwards on the short name parameters:

func soccerGala(announcement: (String, Int) -> String) {
  ...
}

soccerGala { "\($0) ended on place number \($1)" }

Returning closures

So far we only talked about passing closures to a function, but you can also return closures from a function. So let’s try it out by using our soccerGala method, we will rewrite it so that it does not accept a value but does return a closure. The returned closures must be called with a string and returns nothing.

func soccerGala() -> (String) -> Void {
    return {
        print("\($0) is the best soccer team")
    }
}

let bestTeams = soccerGala()
bestTeams("PSV")

I understand that this looks extremely confusing, especially because of the two ‘->’. But if you take the first line down into parts it is actually not that difficult to understand at all.

func soccerGala() -> (String) -> Void

If you replace this line with this line then all of a sudden it doesn’t look that confusing anymore:

func soccerGala() -> String

Our original declaration does not mean anything else then:

func soccerGala() -> (String) -> Void
– declare a function with the name soccerGala

func soccerGala() -> (String) -> Void
– do not allow any parameters to be past into this function

func soccerGala() -> (String) -> Void
– return a closure, which is (as we know) a function by itself. And this closure accepts a String as input parameter and returns no value.

So this means that whenever you call the function ‘soccerGala’ you get a function back. So by calling the function through ‘let bestTeams = soccerGala()’, the variable ‘bestTeams’ is now assigned to the closure that gets returned by soccerGala.

The variable bestTeams has become a function by itself which accepts a String as a parameter but does not return any values.

Calling bestTeams(“PSV”), results into writing the text ‘PSV is the best soccer team’ onto the console. This is done through the print statement of the closure where ‘$0’ gets replaced by the first parameter received by the closure which is “PSV”.

Capture values in a closure

If we look back at the last example we used (returning closures) then we call the function ‘soccerGala’ to get a closure back which we can then call freely.

But closure capturing happens when we create values inside soccerGala which get used inside the closure. So we can extend our code to keep track how often PSV is the best soccer team.

func soccerGala() -> (String) -> Void {
    var priceWon = 24

    return {
        if $0 == "PSV" {
          priceWon += 1
          print("\($0) is the best soccer team for the \(priceWon) time")
        } else {
          print("impossible, something went wrong")
        }
    }
}

let bestTeams = soccerGala()
bestTeams("PSV")
bestTeams("PSV")

So you will see that each time you call the closure the value of “priceWon” will increase by one (that is… when PSV is used as argument ;-)). Even though the priceWon variable is part of the soccerGala function and not of the closure, but we can keep increasing them because the closure captures the value.

Summary

So that is all I had to say about closures for now, there is a lot of information about closures online but the reason to write a post my self on it is because I kept struggling with them and sometimes it is then better to write about them your self so that you keep remembering them better. However, besides my own benefit I also hope it helped you of course in better understanding them.

The most important take aways from this post are:

  • Closures can be assigned to variables so that you can call them on a later time
  • Closures can accept parameters and return values like any other type of function
  • You can even pass a closure into a function as a parameter and this closure can even have parameters by itself and return values.
  • If the last parameter to a function is a closure you can use a trailing closure syntax.
  • Swift accepts shorthand parameter names like $0, $1.
  • If you use external values inside your closure then they will be captured so the closure can refer later to them


Leave a comment

Blog at WordPress.com.

Up ↑