Swift: Functions & Closures

If you are new to Swift and functional programming in general (like me) understanding functions and closures can be confusing. This article aims to clear up any of that confusion.

Right off the bat, it’s important to understand some of the unique properties of functions and closures in Swift. If you’re familiar with Java (like me), then you might be thinking “Functions are just like methods!”, but you’d only be half right. In Swift, functions can actually be assigned to variables, and they can be passed in and out of other functions as arguments.

ŎםŎ say what now? they CAN??

If you’ve never heard of this before (like me) then I will allow you some time to clean your brains off the floor.

Back? Okay! In this way, functions in Swift are treated as “first-class objects”. Just like an Int or a String can be assigned to a variable, a function can also be assigned to a variable. Another important thing to understand about functions in Swift, is that they can “capture” variables that exist outside of their local scope. More on this later…

Now, suppose you wanted to create one of these fancy functions. There’s actually two ways you could go about doing this, depending on the situation. Functions can be declared with the func keyword OR simply with { } which is known as a “closure expression”. We’ll take a look at some examples later…

So, you might be asking yourself “What is the big deal of being able to treat functions like this? How does it benefit me?” As demonstration, consider an array in Swift. It has a map function that takes another function in as argument, and applies that function to every element in the array, returning a new array with the results. For example, an Integer doubler:

// Here is a function that will double an int
func doubleInt(i: Int) -> Int { return i * 2 }
// Now, declare an array and run the doubleInt function
let array = [1, 2, 3, 4];
// Output of print: [2, 4, 6, 8]

There are a few other useful functions in array that also take functions as arguments.

  1. filter (Takes in a function that will test each element for inclusion in the resulting array)
  2. sort (Takes in a function that will determine a custom ordering for two elements)
  3. reduce (Takes in a function that will incorporate each element into a running value. For example, find the sum of all values, find the max/min, etc.)

Now, prepare yourself for some deep, Inception level, recursion magic… You can set the return of a function to be not just any variable like Int or String, but another function itself! Let’s see an example:

// Declare a function that returns another function
func returnFunc() -> (Int) -> () {
func innerFunc(i: Int) {
println("\(i) was passed to returnFunc")
return innerFunc
// Now let's assign the returnFunc to a variable before we call it
// Just because we can =P
let f = returnFunc
f(2) // This will print "2 was passed to returnFunc"

Remember when we talked about closures? Let’s dive into that a bit more. So we know that it’s a way to declare a function and it uses this syntax { }. Remember our doubleInt function? We declared it using the closure syntax:

func doubleInt(i: Int) -> Int { return i * 2 }

This is really nice, concise way of writing code. We don’t have to declare the function separately, taking up unnecessary lines of code. Just like we wouldn’t write:

var sum = 1 + 1

When we could simply have it all in one line like:

someFunction(1 + 1)

In this way, closures let us write cleaner code. However, the rabbit hole only gets deeper from here. So, let’s take our doubleInt function from earlier:

func doubleInt(i: Int) -> Int { return i * 2 }

We can actually write this statement like this instead:

[1, 2, 3].map { $0 * 2 }
°Д° whoa buddy! hold the phone! how did we do that??

Let me explain.

  1. Since all the types can be inferred by Swift, there’s no need to explicitly declare them. Giving us: [1, 2, 3].map( { i in return i * 2 } )
  2. And since our closure expression is a single expression, Swift will infer that we want to return the value of the expression. Giving us: [1, 2, 3].map( { i in i * 2 } )
  3. Swift (being the nice pal that it is) provides us shorthand notation for the arguments to a function ($0 for the first argument, $1 for the second, etc.) Giving us: [1, 2, 3].map( { $0 * 2 } )
  4. When the last argument for a function is a closure expression, we’re allowed to move the expression outside of the parenthesis of the calling function. This comes more in handy when we have a multi-line expression, as it will be easier to read. Nevertheless, it gives us: [1, 2, 3].map() { $0 * 2 }
  5. Finally, since our calling function actually has no arguments other than the closure expression, we don’t even need to include the parethesis. Giving us: [1, 2, 3].map { $0 * 2 }

Neato! Hopefully this gives you a better understanding on functions and closures, and how to use them in Swift.

Like what you read? Give Daniel Kuhlwein a round of applause.

From a quick cheer to a standing ovation, clap to show how much you enjoyed this story.