What is Closure in swift ?
Closure are the self contained blocks of functionality that can pass around and used in the code. it is function without a func keyword.
Consider a function as shown in below snippet Code
func subtwonumber(n1: Int,n2: Int) -> Int {
return n1 - n2
}
let sub = subtwonumber(n1: 10,n2: 5)
print(sub)
OUTPUT 5
In Swift function are the first class citizen so we can assign the variable to the function as shown below
var subnumbers = subtwonumber
let sub = subnumbers(15,5)
print(sub)
OUTPUT 10
Closure works on the same condition as the function as assign to the variable. In closure the in keyword defines the which specifies the parameter before in keyword which is known as closure defination and logic is defined after the in keyword.
let us see the closure defination .This function can be changed to the closure function as shown in below closure.
var subtwonumber: (Int,Int) -> Int = { (n1, n2) in
return n1 - n2
}
let sub = subtwonumber(10,4)
print(sub)
OUTPUT 6
Closure can be written in Different type context , Here we will demonstrate how we can write the closure in different type of context. This is the power of closure which reduces the line of code
Consider a array which has the list of animal
let animal = ["Dog", "Cat", "horse", "Elephant", "camel"]
// Inferring Type From Context
sortanimal = animal.sorted(by: { s1, s2 in return s1 > s2})
//Implicit Return from Single Expression Closure
sortanimal = animal.sorted(by: { s1, s2 in s1 > s2})
//Shorthand argument
sortanimal = animal.sorted(by: { $0 > $1})
//Operator Method
sortanimal = animal.sorted(by: > )
When to use Closure?
Closure are used when there is heavyweight operation such like API parsing , higher order function and completion handler. Closure performs the operation parallely and return the output data based on the operations.
Closure are mainly used for only 2 reason
- Higher Order function
- Completion Handler
Syntax of Closure
{ (parameter) -> return type in
statements
}
it contains the parameter which are passed and the return type and statement which contains the logic
It Consist of 3 mode of argument they are
- Return Type
- Parameter
- in (Optional)
Types of Closure
1. Simple Closure :
Simple Closure is the closure which passes a argument and return the logical statement or paramter after performing the certain operation by it.
Syntax
let returntype: (String) -> String = { name -> in
return name
}
Example
var name = {
print("Hello, World!")
}
2. Closure With Parameter
Closure with parameter consist of parameter enclosed in the braces . Here Parameter should be passed in the argument. The Parameter can be of different data types
Syntax
let returntype: (String) -> () = { parameter -> in
// Statement
}
Example :
let user = { (name: String ) in
print("(name)"
}
Closures can take parameters similar to functions. Here’s an example of a closure with a parameter in Swift:
let greetClosure = { (name: String) -> String in
return "Hello, \(name)!"
}
let greeting = greetClosure("John")
print(greeting) // Output: Hello, John!
In this example, we define a closure greetClosure
that takes a String
argument name
and returns a greeting message that includes the name. We specify the parameter type using the (name: String)
syntax inside the curly braces. We also specify the return type using the ->
arrow notation, followed by the String
type. Inside the closure, we use string interpolation to include the name
argument in the greeting message, and return the result.
To call the closure, we pass in the argument "John"
, which gets substituted for the name
parameter inside the closure. We store the result in a variable called greeting
and print it to the console, which outputs Hello, John!
.
You can also pass closures with parameters as arguments to functions, and use them as a parameter type in a function. Here’s an example of a function that takes a closure with a parameter as an argument:
func repeatAction(times: Int, action: (Int) -> Void) {
for i in 1...times {
action(i)
}
}
let printClosure = { (index: Int) -> Void in
print("Index is \(index)")
}
repeatAction(times: 3, action: printClosure)
// Output:
// Index is 1
// Index is 2
// Index is 3
In this example, we define a function repeatAction
that takes an Int
argument times
and a closure action
that takes an Int
argument and returns Void
. Inside the function, we loop through the range 1...times
and call the action
closure with the loop index.
We then define a closure printClosure
that takes an Int
argument index
and prints a message that includes the index. We pass this closure as the action
argument to repeatAction
, along with the argument 3
. The printClosure
closure gets called three times with the values 1
, 2
, and 3
, and each time it prints a message to the console, which outputs:
Index is 1
Index is 2
Index is 3
I hope this helps! Let me know if you have any further questions.
3.Closure With Return Type
In this Type of closure the consist of parameter with return type .Here parameter is passed to get the return type from the closure
let returntype: () -> String = {
return paramter
}
Example :
let user = { (name: String ) -> String in
return name
}
In Swift, closures can also have a return type. This allows you to write closures that perform some computation and return a value, similar to functions.
Here’s an example of a closure with a return type in Swift:
let multiplyClosure = { (a: Int, b: Int) -> Int in
return a * b
}
let result = multiplyClosure(5, 10)
print(result) // Output: 50
In this example, we define a closure multiplyClosure
that takes two Int
arguments and returns their product as an Int
. We specify the return type using the ->
arrow notation, followed by the Int
type. Inside the closure, we use the return
keyword to return the product of the two arguments.
To call the closure, we pass in the two arguments 5
and 10
, and store the result in a variable called result
. We then print the result to the console, which outputs 50
.
You can also pass closures with return types as arguments to functions, and use them as a parameter type in a function. Here’s an example of a function that takes a closure with a return type as an argument:
func operateOnNumbers(a: Int, b: Int, operation: (Int, Int) -> Int) -> Int {
return operation(a, b)
}
let addClosure = { (a: Int, b: Int) -> Int in
return a + b
}
let result = operateOnNumbers(a: 5, b: 10, operation: addClosure)
print(result) // Output: 15
In this example, we define a function operateOnNumbers
that takes two Int
arguments a
and b
, and a closure operation
that takes two Int
arguments and returns an Int
. Inside the function, we call the operation
closure with the two arguments a
and b
, and return the result.
We then define a closure addClosure
that adds two Int
arguments and returns their sum as an Int
. We pass this closure as the operation
argument to operateOnNumbers
, along with the arguments 5
and 10
. We store the result in a variable called result
and print it to the console, which outputs 15
.
4. Closure pass as function parameter
The closure which is passed in the function call itself .
func closureTest(clos() -> ()) {
print("hello")
}
closureTest(clos: { print("world") } )
In Swift, closures can be passed as function parameters just like any other data type such as integers, strings, or arrays. This allows for greater flexibility and customization in writing functions that perform specific operations on data.
Here’s an example of a function that takes a closure as a parameter:
func performOperation(_ operation: (Int, Int) -> Int, a: Int, b: Int) -> Int {
return operation(a, b)
}
let addClosure = { (a: Int, b: Int) -> Int in
return a + b
}
let result = performOperation(addClosure, a: 5, b: 3)
print(result) // Output: 8
In this example, the performOperation
function takes a closure that performs an arithmetic operation on two integers, as well as two integer parameters a
and b
. The closure is passed as the first argument to performOperation
, along with the two integer parameters. Inside performOperation
, the closure is executed with the integer parameters, and the result is returned.
The closure itself is defined outside the function as a constant variable addClosure
, which takes two integer parameters and returns their sum.
When performOperation
is called with the addClosure
closure and the integers 5 and 3, the closure is executed with those parameters, and the result 8 is returned and printed to the console.
This is just one example of how closures can be used as function parameters in Swift. By passing closures as parameters, you can write more flexible and customizable functions that can be used in a variety of contexts.
5. Trailing Closure
The closure that comes after the function call parentheses is known as the trailing closure. Rather of giving the parameter to the function, we pass it afterward.
if you need to pass a closure expression to a function as a function’s final argument and the closure expression is long it can be usefull to write it as a trailing closure instead. You write the trailing closure after the function call parenthesis , even though the trailing closure is still an argument to the function.
Here the function expects the closure at the end of the parameter list. It expect the closure as the argument.
func someFunction(closure:{
})
Example:
hey{
print (“hey lead by code”)
}
In Swift, a closure is a self-contained block of code that can be passed around and executed later on. Trailing closures are a syntax feature in Swift that allow you to write closures inline as the last argument of a function, rather than having to pass them as a separate argument.
Here’s an example of a function that takes a closure as its last argument, and how you would use a trailing closure to call that function:
func performTask(with delay: TimeInterval, completionHandler: () -> Void) {
// Perform some task with the given delay
DispatchQueue.main.asyncAfter(deadline: .now() + delay) {
// Call the completion handler after the task is done
completionHandler()
}
}
// Call performTask using a trailing closure
performTask(with: 2.0) {
print("Task completed!")
}
In this example, the performTask
function takes a delay
parameter and a closure completionHandler
as its last argument. We can use a trailing closure to call performTask
by passing in the delay as the first argument, and then writing the closure inline as the last argument, without having to wrap it in parentheses.
Trailing closures can make your code more concise and readable, especially for functions that take a closure as their last argument. They’re a useful feature to keep in mind when writing Swift code.
6. Non-Escaping closure :
The non-escaping closure is used by default for the majority of the closures. All of the closures after Swift 3 are known as non-escaping closures.
When we give the argument arguments to a non-escaping closure function, the function conducts the appropriate operation and executes the result in the function body.
Consider a non escaping closure
func calculate_Sum_of_OddNumbers(_ number: [Int], handler:((Int) -> Void)){
var sum = 0
for i in number{
if (number % 2) != 0 {
sum = sum + i
}
}
handler (sum)
}
func total(){
var array = [10,11,12,13,14,15,16,17,18,19]
calculate_Sum_of_OddNumbers(array) { (sum) in
debugPrint(sum)
}
}
total()
7. Escaping Closure:
Escaping closure are always denoted by @escape . Here function passes a argument but preform the operation but it is called before it returns the value.It takes the parameter but returns the void. In this type of closure the function performs the body before getting an argument. Here we Preserve the closure and execute it later. The Scope of the Escaping closure is till untill the closure gets executed and its exitance in memory till it execute the body .
Consider a Escaping closure
func calculate_Sum_of_OddNumbers(_ number: [Int], handler:((Int) -> Void)){
var sum = 0
for i in number{
if (number % 2) != 0 {
sum = sum + i
}
}
handler (sum)
}
func total(){
var array = [10,11,12,13,14,15,16,17,18,19]
calculate_Sum_of_OddNumbers(array) { (sum) in
debugPrint(sum)
}
}
total()
Consider a Asynchronous Execution of the code which consist of delay which handle the asynchronous call for the closure.
func calculate_Sum_of_OddNumbers(_ number: [Int], handler: @escaping ((Int) -> Void)){
var sum = 0
for i in number{
if (number % 2) != 0 {
sum = sum + i
}
}
Global.delay(0.4, closure: {
handler (sum)
})
}
func total(){
var array = [10,11,12,13,14,15,16,17,18,19]
calculate_Sum_of_OddNumbers(array) { (sum) in
debugPrint(sum)
}
}
total()
Life Cycle of Escaping Closure
- Closure is pass as function argument , during the function call
- does the additional work in function
- execution of function call in closure asynchronously or stored
- returns the function call compliler back
8. AutoClosure
AutoClosure : A Closure marked with @Autoclosure keyword is known as autoclosure. AutoClosure Does not accept any argument
An autoclosure is defined with the @autoclosure
attribute, which tells the compiler to automatically create a closure for the argument. When the function is called, the expression is evaluated and wrapped in a closure, which is then passed as the argument to the function.
The benefit of using an autoclosure is that it allows you to defer the evaluation of an expression until it is actually needed. This can be useful for performance reasons, especially when the expression is expensive to compute or has side effects that should only occur if the expression is actually used.
Here’s an example of using an autoclosure in Swift:
func printIfTrue(_ predicate: @autoclosure () -> Bool, message: String) {
if predicate() {
print(message)
}
}
printIfTrue(2 + 2 == 4, message: "This message will be printed because the predicate is true.")
In this example, the printIfTrue
function takes an autoclosure predicate
that returns a boolean value, and a message
string. The predicate
is automatically wrapped in a closure and evaluated when it is called inside the function body. If the predicate is true, the message
string is printed to the console.
Note that the use of @autoclosure
is not always necessary, and should be used judiciously. In general, it is best to use autoclosures only when the performance benefits outweigh the potential readability and maintainability concerns that can arise from using them.