Closures — Blocks Swift

Unlocking Swift Closures: Code Blocks with Impact

Discover practical insights and hands-on examples for leveraging code blocks with impact.

Kanagasabapathy Rajkumar
4 min readNov 19, 2023

What is Closure?

Group code that executes together, without creating a named function.

Closures are self-contained blocks of functionality that can be passed around and used in your code.

In simpler words,

  1. Closures are Callback patterns or communication patterns.
  2. Reference Types
  3. In Objective-C, we call it Blocks.

This article has been updated and moved to my website here

Closure Expression

Anonymous function or block of code

  • Assigned to a variable
  • Passed to function
  • Return the closure
{ (<#parameters#>) -> <#return type#> in

}

Benefits

  • Capture and Store references
  • Capture Lists — Useful for memory management

Good example — sorted function

let names = ["Lorem", "ipsum", "dolor", "sit", "amet"]

func reverseNames(_ s1: String, _ s2: String) -> Bool {
return s1 > s2
}

Capturing Values

Closure can capture constants and variables from the context.

The simplest form of closure that captures values is a nested function. A Nested Function can capture values from the outer function and inside the function.

func addCount(forCount amount: Int) -> () -> Int {
var total = 0
func counter() -> Int {
total += amount
return total
}
return counter
}

// Counter doesn't have any paramter but returns Int value
// Capturing by reference
// Swift handles memory management involved for Capturing Values
let countByTen = addCount(forCount: 10)
print(countByTen()) //prints 10

Functions and Closure are Reference Types

let addCountbyTen = countByTen
print(addCountbyTen()) // prints 20
print(countByTen()) // prints 30

Trailing Closures

Can be called Special Syntax. Rather than passing in your closure as the parameter, you can pass after the function inside the braces()

If you need to pass a closure expression to a function as the function’s final argument and the closure expression is long, it can be useful to write it as a trailing closure instead.

Way to make your code look cleaner and more readable when using functions or methods that take closures as arguments.

Making your code more natural

Good example —Sorted Example: names.sorted { $0 > $1}

func makeAddOfTwoNumbers(digits: (Int, Int), onCompletion: (Int) -> Void) {
let sum = digits.0 + digits.1
onCompletion(sum)
}
makeAddOfTwoNumbers(digits: (7,9)) { sum in
print(sum)
}

Escaping Closures

@escaping attribute Preserved to be executed later.

Life Cycle

  • Pass the closure as a function argument
  • Do additional work in func
  • Func execute async or stored
  • Func returns the compiler back

Benefits:

  • Storage — Preserve the closure in storage in memory, part of the calling function gets executed and returns the compiler back
  • Asynchronous Execution — Executing the closure async on dispatch Queue, queue will hold the closure in memory, to be used for future. — No idea of when the closure gets executed
let url = URL(string: "https://swiftpublished.com/")!
let data = try? Data(contentsOf: url)

// Function to call the escaping closure
func loadData(completion: @escaping (_ data: Data?) -> Void) {
DispatchQueue.global().async {
let data = try? Data(contentsOf: url)
DispatchQueue.main.async {
completion(data)
}
}
}
loadData { data in
guard data != nil else {
// Handle error
return
}
}

Non-Escaping Closures

They are Default Closures. Preserved to be executed immediately.

Lifecycle

  • Pass the closure as a function argument
  • Do additional work and execute
  • Function returns

Benefits:

  • Immediate Callback
  • Synchronous Execution
func getSum(of array: [Int], completion: (Int) -> Void) {
var sum: Int = 0
for i in 0..<array.count {
sum += i
}
print(sum)
completion(sum)
}
getSum(of: [1,2,3,4,5]) { sum in
print("SumArray \(sum)")
}

Autoclosures

Automatically created to wrap a piece of code. Delay the execution of closure until it is actually needed.

Uses:

  • When Closure is optional or
  • Where closure’s execution may not be necessary

Swift Standard Library

  • assert(_:_:file:line:)
  • precondition(_:_:file:line:)
  • assertionFailure(_:_:file:line:)
func logIfTrue(_ condition: @autoclosure() -> Bool) {
if condition() {
print("True")
} else {
print("False")
}
}

logIfTrue("Saba" > "pathy")

Autoclosure with @escaping attribute

func delayedPrint(delay: Double, closure: @escaping @autoclosure () -> Void) {
DispatchQueue.main.asyncAfter(deadline: .now() + delay) {
closure()
}
}
print("Start 2")
delayedPrint(delay: 5, closure: print("Hellow Saba"))

Did you Know?

  • Till Swift 2, Escaping closures — default
  • Because of performance and code optimisation.
  • Closures are Reference Types
  • Shorthand arguments can be used — $0, $1
  • Alternative to Delegation Pattern
  • Closure with typealias acts as a reusable
func execute(closure: SimpleClosure) {
print("Executing Closure")
closure()
}
let helloClosure: SimpleClosure = {
print("Hello World")
}
execute(closure: helloClosure)

Please find the complete playground -> https://gist.github.com/sabapathyk7/525ae7cce276488de19a8cc7514fc6e4

Grateful for your read, now let’s code on!

Interested in connecting? 
Feel free to connect with me on: LinkedIn and GitHub

Please take a look at my first-ever Article 👇

--

--

Kanagasabapathy Rajkumar

Swift Enthusiast | Building Seamless iOS Experiences 🚀 | Swift & Objective-C |