Closures in Swift
Interactive Online Courses: Elevate Skills & Succeed Enroll Now!
Closures are blocks of code that execute together but without a named function. These are similar to lambdas in other programming languages. We define it without using the func keyword. It can accept parameters and return values just like functions.
In this article, we’ll learn about the basics of closures and how we can use them as function parameters or operator functions. We’ll also go through other related concepts along with relevant examples.
Declaration and Calling Closure in Swift
We define a closure without the func keyword and a function name. We can assign closures to variables or constants. We optionally use the in keyword to separate the return type from the body of the closure.
Syntax for closures.
{ (parameters) -> returnType in
//code
}We call a closure by writing the name of the variable it is assigned to, followed by parentheses ().
Unlike functions, we don’t need to write the parameter name.
For example,
//Declaring a closure
var greetClosure = { (name: String) in
print("Hello from \(name)!")
}
//Calling the closure
greetClosure("DataFlair")Output:
Hello from DataFlair!
Closure Parameters
Closures can also accept parameters. Unlike in functions, while calling the closures, we don’t need to write the argument label. For example,
var sum = { (num1: Int, num2: Int) in
print("Sum: \(num1 + num2)")
}
sum(20, 23)Output:
Sum: 43
We can also define closures without parameters.
let noParameterClosure = { () in
print("Hello from DataFlair!")
}
noParameterClosure()Output:
Hello from DataFlair!
Closure Return Value
Closures can have a return value just like a function. We need to specify the return type before returning a value. For example,
var productClosure = { (num1: Int, num2: Int) -> Int in
return num1*num2
}
var answer = productClosure(21, 34)
print("The product is: \(answer)")Output:
The product is: 714
Closure as Function Parameter
We can pass a closure as a parameter in a function. For example, we can create closures for mathematical operations, and those closures can be passed as a parameter in a function.
func mathOperation(num1: Int, num2: Int, operation: (Int, Int) -> Int) -> Int {
return operation(num1, num2)
}
let add = { (x: Int, y: Int) -> Int in
return x + y
}
let subtract = { (x: Int, y: Int) -> Int in
return x - y
}
let multiply = { (x: Int, y: Int) -> Int in
return x * y
}
let addResult = mathOperation(num1: 23,num2: 20, operation: add)
let subtractResult = mathOperation(num1: 23,num2: 20, operation: subtract)
let multiplyResult = mathOperation(num1: 23,num2: 20, operation: multiply)
print("Add Result: \(addResult)")
print("Subtract Result: \(subtractResult)")
print("Multiply Result: \(multiplyResult)")Output:
Add Result: 43
Subtract Result: 3
Multiply Result: 460
Shorthand Argument Names
Shorthand Arguments name refers to the parameters in the function concisely. It gives placeholders like $0 to the first parameter, $1 to the second and so on. Swift infers these automatically. It makes our code shorter. We can use these when we are writing closures for operations like filtering, sorting or mapping.
For example, we write a closure with shorthand argument names to create an even numbers array from an existing array.
let numbers = [41, 42, 43, 45, 47, 48, 49]
let evenNumbers = numbers.filter { $0 % 2 == 0 }
print(evenNumbers)Output:
[42, 48]
Operator Methods
We can use closures to create operator methods to enable custom behaviour for existing operators. These operators can be arithmetic, comparison operators or assignment operators. For example, we create a closure to sort the elements in ascending order.
let numbers = [12,7,34,5,62,3,21] let sortedNumbers = numbers.sorted(by: <) print(sortedNumbers)
Output:
[3, 5, 7, 12, 21, 34, 62]
Trailing Closure
In a trailing closure, we write the closure expression after the function call’s parentheses. We can use this only when the closure is the last argument in the function. For example, Arrays in Swift use trailing closures in its method. One such method is the map(_:) method. This method accepts closure expressions as the only parameter.
Another example of trailing closure is as follows.
func addOperation(num1: Int, num2: Int, operation: (Int) -> Void) {
let sum = num1 + num2
operation(sum)
}
addOperation(num1: 23, num2: 37) { answer in
print("Sum is \(answer).")
}Output:
The sum is 60.
Autoclosure
Autoclosure automatically wraps the closure expression and postpones its evaluation until it is accessed. We can pass these without using braces when we call the function. We need to follow the syntax of including the @autoclosure keyword in the function definition. @autoclosure is an attribute that is applied to the parameters of a function.
Note that we can encounter errors when we pass arguments to autoclosure.
//function that takes autoclosure
func printGreeting(greeting: @autoclosure () -> String){
print(greeting())
}
//Calling function with autoclosure
printGreeting(greeting: "Hello from DataFlair!")Output:
Hello from DataFlair!
Capturing Values
We can capture and store values with the help of closures. We can refer to and modify these values inside the closure even if the variable or constant holding the values does not exist.
For example, we have a function makeCounter(). It returns the closure increment() that captures the value of the count variable. When we call makeCounter(), the value of the count is increased by 1.
func makeCounter() -> () -> Int {
var count = 0
let increment: () -> Int = {
count += 1
return count
}
return increment
}
let counter = makeCounter()
for i in 0..<5{
print("Count \(i): \(counter())")
}Output:
Count 0: 1
Count 1: 2
Count 2: 3
Count 3: 4
Count 4: 5
Capturing Reference Types
When we are dealing with reference types, closures capture the references to the instances.
For example,
class Example{
var arrayExample = ["DataFlair", "Swift"]
}
var example1 = Example()
let referenceClosureCapture = {
print("Closure Capture for references: \(example1.arrayExample)")
}
referenceClosureCapture()
example1.arrayExample.append("Closure")
referenceClosureCapture()Output:
Closure Capture for references: [“DataFlair”, “Swift”]
Closure Capture for references: [“DataFlair”, “Swift”, “Closure”]
More about Closures
- Global and nested functions are special kinds of closures.
- Closures have a name but don’t capture any values that are Global Functions.
- Closures have a name, and capture values from the function enclosed are Nested Functions.
- Swift closure expressions are optimizations that promote clear and concise syntax in common cases. Some of these optimizations are type inference of parameter and return value, shorthand argument names, and trailing closure. These are explained in detail above.
Conclusion
Swift provides a way to write functions without naming them. It can accept parameters and return variables. We can also assign them to variables and constants. In this article, we’ve discussed the basics of closures. We have also gone through different kinds of closures, like trailing closures and auto closures. We have seen how we can capture values and reference values with the help of closures.
Your 15 seconds will encourage us to work even harder
Please share your happy experience on Google

