Swift Closures

In this article, you'll learn what is a closure, syntax, types of closures in Swift with examples.

In the article Swift functions, we created a function using func keyword. However, there is another special type of function in Swift, known as closures that can be defined without using keyword func and a function name.

Like functions, closures can accept parameters and return values. It also contains a set of statements which executes after you call it and can be assigned to a variable/constant as functions.

Closures are mainly used for two reasons:

  1. Completion blocks
    Closures help you to be notified when some task has finished its execution. See Closure as a completion handler to learn more about it.
  2. Higher order functions
    Closures can be passed as an input parameters for higher order functions. A higher order function is just a type of function that accepts function as an input and returns value of type function as output.
    For this purpose it's better to use closures in replacement of function because closure omits the func keyword and function name that makes the code more readable and short.

Syntax of closure

Closure expression syntax has the following general form:

{ (parameters) -> return type in
   statements
}

Notice the use of in keyword after the return type. The in keyword is used to separate the return Type and statements inside the closure. The closure accepts parameters and can return value. Lets learn to create your own closures with below examples:


Example 1: Simple Closure

let simpleClosure = {   
}
simpleClosure()


In the above syntax, we have declared a simple closure { } that takes no parameters, contains no statements and does not return a value. This closure is assigned to the constant simpleClosure.

We call the closure as simpleClosure(), but since it doesn't contain any statements, the program does nothing.


Example 2: Closure that contains statements

let simpleClosure = {
    print("Hello, World!")
}
simpleClosure()

When you run the above program, the output will be:

Hello, World!

In the above program, you have defined a closure simpleClosure. The type of the simpleClosure is inferred to be of () -> () because it doesn't accept parameter and does not return a value.

If you want to explicitly defined the type of the closure, you can do so as let simpleClosure:() -> ().

Unlike the first example, calling the closure as simpleClosure() executes the print() statement inside it. This prints Hello, World! in the console.


Example 3: Closure that accepts parameter

let simpleClosure:(String) -> () = { name in
    print(name)
}
simpleClosure("Hello, World")

When you run the above program, the output will be:

Hello, World!

In the above program, the type of closure (String) -> () states that the closure takes an input of type String but does not return value.You can use the value passed inside the statements of the closure by placing a parameter name name followed by in keyword.

Remember the use of in keyword is to separate the parameter name with the statements. Since the closure accepts a String, you need to pass the string while you call the closure as simpleClosure("Hello, World"). This executes statement inside the closure and outputs Hello, World! in the console.


Example 4: Closure that returns value

A closure can also return value, as functions. If you need to return value from closure, you must explicitly add the type to return inside braces ()which is followed by ->.

let simpleClosure:(String) -> (String) = { name in
    
    let greeting = "Hello, World! " + "Program"
    return greeting
}

let result = simpleClosure("Hello, World")
print(result)

When you run the above program, the output will be:

Hello, World! Program

In the above program, we have defined type as simpleClosure: (String) -> (String) because Swift cannot automatically infer the closure that returns a value. The type (String) -> (String) states that the closure takes an input of type String and also returns a value of type String.

The closure also returns a value using the return keyword as return greeting and the returned value can be assigned in a variable/constant as let result = as we have learned in Swift functions.


Example 4: Passing Closures as a function parameter

We can also pass closure as a parameter to a function as:

func someSimpleFunction(someClosure:()->()) {

    print("Function Called")
}

someSimpleFunction(someClosure: {
    print("Hello World! from closure")
})

When you run the above program, the output will be:

Function Called

In the above program, the function accepts a closure of type ()->() i.e takes no input and doesn't return any value.

Now when calling the function someSimpleFunction(), you can pass the closure { print("Hello World! from closure") } as a parameter.

The function call triggers the print("Function Called") statement inside the function which outputs Function Called in the screen. However, the closure statement is not executed because you have not made the call to the closure.

You can call the closure simply as someClosure() which executes the statement inside the closure.

func someSimpleFunction(someClosure:()->()) {
    print("Function Called")
    someClosure()
}

someSimpleFunction(someClosure:  {

    print("Hello World! from closure")

})

When you run the above program, the output will be:

Function Called
Hello World! from closure

Trailing Closure

If a function accepts a closure as its last parameter, the closure can be passed similar to a function body between { }. This type of closure written outside of function call parentheses is known as trailing closure.

The above program can be rewritten using trailing closure as:

Example 5: Trailing closure

func someSimpleFunction(msg:String, someClosure:()->()) {
    print(msg)
    someClosure()
}

someSimpleFunction(msg:"Hello Swift Community!")  {
    print("Hello World! from closure")
}

When you run the above program, the output will be:

Hello Swift Community!
Hello World! from closure

In the above program, function someSimpleFunction() accepts a closure as a final parameter. So, while calling the function, instead of passing the closure as an argument, we have used trailing closure.

As you can see, in the function call

someSimpleFunction(msg:"Hello Swift Community!")  {
    print("Hello World! from closure")
}

closure { print("Hello World! from closure") } looks like the body of a function rather than function argument, but remember it's still an argument to the function.

Because of trailing closure, we haven't specified the parameter name for the closure which makes the code shorter and more readable.

It is not mandatory to write trailing closure. However, it is recommended for readability purpose when a function accepts a closure as a final argument.


AutoClosure

A closure which is marked with @autoclosure keyword is known as autoclosure. @autoclosure keyword creates an automatic closure around the expression by adding a {}. Therefore, you can omit braces {} when passing closures to a function.

The main advantage of using autoclosure is that you don't need to wrap the expression in curly braces {} when calling closures.

Let's see this in example.

Example 6: Closure without @autoclosure

func someSimpleFunction(someClosure:()->(), msg:String) {
    print(msg)
    someClosure()
}

someSimpleFunction(someClosure: ({
    
    print("Hello World! from closure")

}), msg:"Hello Swift Community!")

When you run the above program, the output will be:

Hello Swift Community!
Hello World! from closure

In the above program, we have declared a function that accepts a normal closure ()->() as the parameter of the function someSimpleFunction(). You can see, when calling the function, you need to add {}around the function parameter as

someClosure: ({
    print("Hello World! from closure")
})

We can rewrite the above program using autoclosure as:


Example 7: Autoclosure

func someSimpleFunction(someClosure: @autoclosure ()->(), msg:String) {
    print(msg)
    someClosure()
}

someSimpleFunction(someClosure: (print("Hello World! from closure")), msg:"Hello Swift Community!")

When you run the above program, the output will be:

Hello Swift Community!
Hello World! from closure

In the above program, we have marked the closure ()->() to be of type autoclosure with @autoclosure attribute. So, you don’t have to add { } around the function parameter as someClosure: (print("Hello World! from closure")).


Autoclosure with arguments and return value

Like normal closures, you can pass arguments to and return value from an autoclosure. However, even if you pass the arguments, they get ignored and cannot be used inside the closure. This is because you cannot define the parameter to use it as { arg in }.

Therefore, it is recommended to create autoclosures that don't take arguments but can return value. Value is the expression that's wrapped inside of it. Let's see this in below example.

Example 8: Autoclosure with return value

func someSimpleFunction(_ someClosure:@autoclosure ()->(String)) {
    let res = someClosure()
    print(res)
}
someSimpleFunction("Good Morning")

When you run the above program, the output will be:

Good Morning

In the above program, we've defined a function that takes no parameter but returns a String (()->(String)). We passed the autoclosure "Good Morning" to the function. This is also the return value of the closure.

So, when we called someClosure() inside the function, it returned the value Good Morning.

Example 9: Autoclosure with arguments

func someSimpleFunction(_ someClosure:@autoclosure (String)->(String)) {
    let res = someClosure("Hello World")
    print(res)
}
someSimpleFunction("Good Morning")

When you run the above program, the output will be:

Good Morning

In the above program ,we have defined a function that takes an autoclosure. The closure takes a value of type String and also returns value of type String.

Like the previous example, we pass autoclosure "Good Morning" to the function, which is the return value of the closure.

So, even though autoclosure is called as someClosure("Hello World"), it cannot accept any parameters, it still returns and prints Good Morning.


Escaping vs No Escaping Closures

No Escaping Closure

A closure is said to be no escaping when the closure is passed as an argument to the function, and is called before the function returns. The closure is not used outside of the function.

In Swift, all closure parameters are no escaping by default. The concept of escaping and no escaping closure is for compiler optimization.

Example 10: No escaping closure

func testFunctionWithNoEscapingClosure(myClosure:() -> Void) {
    print("function called")
    myClosure()
    return
}


//function call
testFunctionWithNoEscapingClosure {
    print("closure called")
}

When you run the above program, the output will be:

function called
closure called

In the above example, the closure is said to be no escaping because the closure myClosure() is called before the function returns and closure is not used outside the body of the function.


Escaping Closure

A closure is said to escape a function when the closure is passed as an argument to the function, but is called after the function returns or the closure is used outside of the body of the function.

Example 11: Escaping Closure

var closureArr:[()->()] = []
func testFunctionWithEscapingClosure(myClosure:@escaping () -> Void) {
    print("function called")

    closureArr.append(myClosure)
    myClosure()

    return
}

testFunctionWithEscapingClosure {
     print("closure called")
}

When you run the above program, the output will be:

function called
closure called

In the above example, we have declared a variable closureArr that can storeanarray of closures of type ()->().

Now, if we append the closure myClosuredefined within the scope of the function to the closureArr defined outside of the function, the closure myClosure needs to escape the body of the function.

So, the closure needs to be escaping and marked with @escaping keyword.

Example 12: No escaping closure

In the above No Escaping Closure section, we described a closure needs to be no escaping if the closure is called before the function returns. So, if a closure calls after the function returns, that should be escaping, right?

Lets test this with an example:

func testFunctionWithNoEscapingClosure(myClosure:() -> Void) {
    return
    myClosure()
}

The above code returns warning because statements appearing after the return statement(in our case myClosure()) does not execute because the return statement transfers the control of the program to the function caller.

So, how do we test so that the closure is called after the function returns. It can be done if the closure call is placed inside an asynchronous operation.

Synchronous operation waits for the operation to complete/finish before moving to the next statement (top to Bottom order). And, Asynchronous moves to the next statement even if the current operation hasn't completed.

Therefore, statements that is placed inside an asynchronous operation may execute later at some point.

func testFunctionWithNoEscapingClosure(myClosure:@escaping () -> Void) {
    DispatchQueue.main.async {
        myClosure()
    }
    return
}

In the above program, DispatchQueue.main.async runs the block of code asynchronously. So, now. the closure call myClosure() may happen even after the function returns. So, the closure needs to be escaping and is marked with @escaping keyword.


Closure as a completion handler

Completion handler are callbacks/notifications that allows you to perform some action when a function completes its task.

Completion handler are mostly used in asynchronous operations so that the caller can know when the operation has completed to perform some action after the completion of the operation.

Example 13: Closure as a completion handler

func doSomeWork(completion:()->()) {
    print("function called")
    print("some work here")
    print("before calling callback")
    completion()
    print("after calling callback")
}

doSomeWork(completion: {
    print("call back received")
})

print("Other statements")

When you run the above program, the output will be:

function called
some work here
before calling callback
call back received
after calling callback
Other statements

The above program can also be rewritten using trailing closure as:

Example 14: Trailing Closure as a completion handler

func doSomeWork(completion:()->()) {
    print("function called")
    print("some work here")
    print("before calling callback")
    completion()
    print("after calling callback")
}

doSomeWork() {
    print("call back received")
}

print("Other statements")

How completion handler works?

Closure as a completion handler

Here are the execution steps:

  1. doSomeWork()calls the function which executes the statement inside the function
  2. print("function called") outputs function called in the console.
  3. You may perform some work inside the function. For now just a print("some work here") statement that outputs some work herein the console.
  4. A simple call to the closure as completion() will send the callback and transfers the control of the program to the statements inside the closure.
    So, print("call back received") executes which outputs call back received in the console.
  5. After that the program control again returns to the closure call, and executes statement print("after calling callback") which outputs after calling callback in the console.
  6. After the statements inside the function executes, program transfers the control to the function call doSomeWork()and then executes the next statement print("Other statements") which outputs Other statements in the console.

Let's see one more practical example to use closure as a completion handler.


Example 11: Closure as a completion handler

var closeButtonPressed = false
func codeinPlayground(completion:(String)->()) {
    
    print("Code, Take a Nap and Relax")
    if closeButtonPressed {
        completion("Close the playground")
    }
}

codeinPlayground { (msg) in
    print(msg)
}

When you run the above program, the output will be:

Code, Take a Nap and Relax

In the above program, we have declared a variable closeButtonPressed that mocks if user has tapped close button on playground. Think if you press the close button, variable closeButtonPressed will be true.

The function codeinPlayground accepts a closure as an argument. The closure completion accepts a String but does not return a value. Since the closeButtonPressed is assigned false, statement inside if statement does not execute and closure is not called.

Now, if you assign the closeButtonPressed to true (i.e., when user pressed close button) as var closeButtonPressed = true, statements inside if executes and closure is called.

When you assign true to variable closeButtonPressed, the output will be:

Code, Take a Nap and Relax
Close the playground

Here, we have used closure as a completion handler because when user taps the close button, we don't want to execute statements inside the function codeinPlayground, instead complete its execution by calling the closure completion("Close the playground").

This way we get a chance to handle all the final events related to the function inside the statements of the closure. In our case we have output the message in console as print(msg).