Swift How to Pass a Function as a Parameter

To pass a function as a parameter into another function in Swift, the accepted parameter type needs to describe a function.

For instance, this function accepts two regular parameters and a function parameter:

func operate(x: Double, y: Double, function: (Double, Double) -> Double) {
    return function(x, y)
}

Now you can call this function by passing it an argument which is any function that takes two doubles and returns a double.

For instance, let’s create a function that adds two numbers and pass it as an argument to the function operate():

func add(number1: Double, number2: Double) -> Double {
    return number1 + number2
}

operate(x: 1, y: 2, function: add)

Output:

3.0

This is the quick answer.

However, there is a lot more you need to know when passing functions as arguments in Swift.

In this guide, you learn how to:

  • Write a function that accepts a function argument.
  • Pass a regular function into a function.
  • Pass a closure function into a function.

How Does Passing a Function as an Argument into Another Function Work

So far you have seen functions that take arguments with a specific type, such as String or Int.

For example, a function that greets a person takes an argument name of type String:

func greet(name: String) {
    print("Hello \(name)")
}

greet(name: "Alice")

Output:

Hello Alice

This means that calling this function is only possible by giving it a string argument.

However, any function in Swift also has a type.

For instance, the type of this function:

func multiply(_ x: Double, _ y: Double) -> Double {
    return x * y
}

Is:

(Double, Double) -> Double

As it takes two Doubles and returns a Double.

Just like String, Int, Double, or any other type in Swift, (Double, Double) -> Double is also a valid type.

This type describes a function, instead of a single value.

It is ok to specify a function argument whose type is a function.

When you call a function with a function argument, the type of argument function must match the type of argument type specified in the function definition.

This works the same way as passing any other parameter into a function.

Let’s create a function called calculate(). This function takes two arguments which are Doubles. It also accepts a function argument, that operates on these two doubles and returns a double.

In other words, the calculate() function does not care what the argument function does, as long as it takes two Doubles and returns a Double.

func calculate(_ x: Double, _ y: Double, _ function: (Double, Double) -> Double) {
    print(function(x,y))
}

Next, let’s come up with some functions of type (Double, Double) -> Double and pass them as arguments into the calculate() function call.

func add(a: Double, b: Double) -> Double {
    return a + b
}


func subtract(a: Double, b: Double) -> Double {
    return a - b
}


func multiply(a: Double, b: Double) -> Double {
    return a * b
}


func divide(a: Double, b: Double) -> Double {
    return a / b
}

calculate(10, 20, add)
calculate(10, 20, subtract)
calculate(10, 20, multiply)
calculate(10, 20, divide)

Output:

30.0
-10.0
200.0
0.5

As you can see, it works regardless of which function you call as long as the type of the function is correct.

However, commonly this type of code tends to be messy and has more code than you would actually need.

To get rid of the excess lines of code, let’s take a look at how to use closures in Swift.

Closures – Specify the Argument Function Inline

If a function takes another function as an argument in Swift, you do not need to specify a separate function and pass that as an argument.

Instead, you can use a closure function.

In Swift, a closure follows this syntax:

{ (parameters) -> return type in
    statements
}

A closure is an anonymous function. It is a block of code that acts as a function but does not have a name.

Using a closure it is possible to implement an argument function directly in the function call.

If this sounds strange, let me show you how it works.

Let’s continue with calculate() function from the previous example:

func calculate(_ x: Double, _ y: Double, _ function: (Double, Double) -> Double) {
    print(function(x,y))
}

In the previous example, you wrote 4 separate functions: add, subtract, multiply, and divide. Then you passed these functions as the arguments to the calculate() function call.

However, you can just as well specify the function directly into the calculate() function as a closure:

calculate(10, 20, {(n1: Double, n2: Double) -> Double in 
    return n1 + n2
})

This piece of code sums the input arguments 10 and 20 using the closure function specified in the 3rd argument.

The above piece of code is equivalent to:

calculate(10, 20, add)

func add(a: Double, b: Double) -> Double {
    return a + b
}

Before talking about why closures are useful, let’s simplify the closure a bit.

How to Simplify the Closure

simplifying closure in Swift

Let’s go back to the closure you saw in the previous chapter:

calculate(10, 20, {(n1: Double, n2: Double) -> Double in 
    return n1 + n2
})

This closure works just fine.

However, because the Swift compiler is quite clever, we can simplify the closure by a lot.

Here is the step-by-step guide to simplifying a closure with the above piece of code as an example.

1. Implicit Return Type

First of all, the Swift compiler can automatically understand what the return type of the closure is. So you do not need to specify the return type of the closure.

calculate(10, 20, {(n1: Double, n2: Double) in 
    return n1 + n2
})

2. Implicit Parameter Type

Also, the compiler can deduce the types of closure arguments without you specifying them.

calculate(10, 20, {(n1, n2) in 
    return n1 + n2
})

3. Implicit Return

Then, the Swift compiler knows you want to return the result without having to use the return keyword. Now the expression is already short enough to “fit” on a single line.

calculate(10, 20, { (n1, n2) in n1 + n2 })

Output:

30.0
-10.0
200.0
0.5

4. Implicit Parameter Naming

Last but not least, the Swift compiler is clever enough to let you simplify even further. It automatically stores the parameters of the function argument into variables $0 and $1.

This means you do not even need to specify n1 and n2 in the closure. Instead, you can just type:

calculate(10, 20, { $0 + $1 })

Where:

  • $0 refers to the first argument (n1).
  • $1 refers to the second argument (n2).

And if there were more arguments, these would be assigned to $2, $3, and so on.

As you can see, we were able to simplify the closure quite a bit. This last expression is super easy to read and the intention is clear.

For the sake of completeness, let’s write down the closures for:

  • Adding
  • Subtracting
  • Multiplying
  • Dividing
calculate(10, 20, { $0 + $1 })
calculate(10, 20, { $0 - $1 })
calculate(10, 20, { $0 * $1 })
calculate(10, 20, { $0 / $1 })

Output:

30.0
-10.0
200.0
0.5

Looks way better than this, right?

func add(a: Double, b: Double) -> Double {
    return a + b
}


func subtract(a: Double, b: Double) -> Double {
    return a - b
}


func multiply(a: Double, b: Double) -> Double {
    return a * b
}


func divide(a: Double, b: Double) -> Double {
    return a / b
}

calculate(10, 20, add)
calculate(10, 20, subtract)
calculate(10, 20, multiply)
calculate(10, 20, divide)

This way you can probably start to see why using closures is beneficial in Swift. In the next chapters, we are going to discuss why you should use closure in Swift.

Why Use Closures in Swift?

Imagine you have a function that takes a function as an argument. Furthermore, the argument function is only going to be used once.

In this situation, it would be a waste of code to leave a separate function hanging to the codebase if it is only used once.

This is where using a closure is useful.

Instead of separately specifying the function as the argument function, you can use a closure directly. This way there will be no unusable code left after calling the function.

Let me show you a great example.

A Real-Life Example of a Closure

In Swift, you are going to pass functions as arguments to functions quite often. More often than not, this means you are going to use closures.

A common example is when using the map() function.

In Swift, the map() function transforms one collection into another. It takes a function argument, applies it to each element in the collection, and returns the result.

If you only need the argument function once, it is better to directly specify it as a closure instead of a separate function.

For instance, let’s square an array of numbers:

let numbers = [1, 2, 3, 4, 5]
let squared = numbers.map( $0 * $0 )

print(squared)

Output:

[1, 4, 9, 16, 25]

Here the closure $0 * $0 multiplies each element by itself.

Swift map function with a closure

This code is readable and concise.

Alternatively (but not preferably), you could do the same by calling the map() function with a normal function argument:

Just like we did in the previous chapter. This time, let’s use a simplified closure:

Output:

[1, 4, 9, 16, 25]

However, as you can see, this complicates the code for no reason.

This is why it is usually better to use a closure when passing a function as an argument to another function.

Conclusion

Today you learned how to pass a function as an argument to another function in Swift.

To recap, any function argument in Swift needs to specify the type of the argument.

Commonly you see arguments of type String, Int, or Double.

However, functions have types too, such as (Double, Double) -> Double.

This means a function argument can be a function as well.

To pass a function as an argument to another function in Swift, the function has to accept a function argument whose type matches your function’s type.

When passing functions as arguments to other functions, you can do so by using:

  • A regular function.
  • A closure.

More often than not, using a closure makes the code more readable and saves some lines of code.

Scroll to Top