May 28, 2022 leadbycode

Closure in Swift

 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 

  1. Higher Order function
  2. 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.

Index