Closures In Swift And It’s Related Importance
Closures In Swift And It’s Related Importance
Closures in Swift
Closure is everywhere in Swift. If you are working with Swift you must be using closure.
What is closure?
Closures are self-contained blocks of functionality that can be passed around and used in your code
- Swift Documentation.
Let’s see an example of closure: {Code} let isEven = { (value: Int) -> Bool in return (value % 2 == 0) ? true : false } print("5 is \(isEven(5))") {Code}
Code Snippet – 1
Everything within the brackets { } is called as the Closure expression.
Closure expressions are unnamed closures written in a lightweight syntax that can capture values from their surrounding context
- Swift documentation
Closure expression syntax: { (parameters) -> return type in statement }
In code snippet – 1
- The parameter name is “value” which is of type “Int”.
- The return type is “bool”.
- Statement and parameters are separated by “in” keyword.
There are various optimizations provided to the closure expression.
- Closure expression can Infer types from it context.
Swift infers the type of the parameter and type of the value it returns from the context.
We will modify the closure expression in Code Snippet – 1.
let isEven = { value in return (value % 2 == 0) ? true : false } print("5 is \(isEven(5))")
Code Snippet – 2
In the above code snippet swift automatically infers the type of value and its return type. We don’t need to explicitly define the parameter type as an Int and return type as a bool.
- Single expression closure can implicitly return.
Single expression closure can implicitly return the result without using return keyword.
So, we can skip the return keyword for the closure containing the single expression.
We will modify the closure expression in Code Snippet – 1
let isEven = { value in (value % 2 == 0) ? true : false } print("5 is \(isEven(5))")
Code Snippet – 3
Note: For Closure with multiple expressions we should use return statement explicitly.
- Shorthand Argument names
Swift provides shorthand argument like $0, $1 and so on to refer to the parameters of the closure expression
We will again modify the closure expression in Code Snippet – 1
let isEven = { ($0 % 2 == 0) ? true : false } print("5 is \(isEven(5))")
Code Snippet – 4
In the above code snippet we have replaced the parameter value with $0.
- Trailing Closure
Whenever we are passing closure in the function call and closure expression is long, we can write the closure expression as trailing closure.
Even if the trailing closure is an argument of the function call it is written after the parentheses of the function.
Note: As the name suggests, Trailing closure should be the last parameter of the function call.
func validateName(name: String, printName: (String) -> ()) { // print name if it contains more than 5 characters if name.count > 5 { printName(name) } } validateName(name: "Peter Parker") { (name) in print("Name with more than 5 characters \(name)") }
Code Snippet – 5
In the above code snippet we pass a closure as the final argument to the validateName function.
Trailing closure adds syntactic sugar and increases the code readability.
Closure can capture values.
Closure can capture and modify the variables and constants from the context in which they are defined.
The value of the captured variable and constants can be modified within the closure even if the original scope that defined the constant and variable no longer exists.
func outerFunc() -> (Int) -> () { var count = 0 func innerFunc(i: Int) { count += i // value of count variable is captured print(count) } return innerFunc // Counter variable should be destroyed here. // But it is kept alive to used by outerFunc. } let storeFunc = outerFunc() storeFunc(7) // prints 7 storeFunc(8) // prints 15 // The count variable is created once and is captured by innerFunc // Everytime innerFunc gets called counter is updated.
Code Snippet – 6
In the above code snippet outerFunc returns a function which takes an Int as a parameter.
innerFunc captures the variable count which is defined within outerFunc and it is not destroyed when the scope of the outerFunc ends.
Escaping Closures
When the closure is passed to a function which gets executed after the function returns then it is known as an escaping Closures.
In order to make a closure escaping, we add @escaping before the closure type in the function argument
func performTask(completion: @escaping () -> ()) { print("Performing task") // Delay the execution of the closure by 0.5 sec DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { completion() } return } performTask() { print("Completed all task") }
Code Snippet – 7
In the above example, the function “performTask” returns by executing the print statement. While the execution of the closure is delayed by 0.5 secs.
Escaping closure are very useful to mark the completion of an API call or completion of an animation.
Note: By default closure in swift are non-escaping in nature i.e the closure gets executed before the function returns.