Swift Closures Explained: From Beginner to Advanced

Shahriar Hossain
3 min readMar 27, 2024

--

Closures are powerful and versatile constructs in Swift that allow you to encapsulate functionality and pass it around as first-class citizens. In this comprehensive guide, we’ll explore closures from their basic syntax to more advanced techniques, catering to beginners and experienced developers alike.

1. Introduction to Closures

Closures are self-contained blocks of functionality that can be passed around and used in your code. They can capture and store references to any constants and variables from the context in which they are defined, making them incredibly flexible.

let simpleClosure = {
print("Hello, world!")
}

simpleClosure()

// Output: Hello, world!

2. Basic Syntax and Usage

Closures in Swift have a concise syntax. Here’s a simple example of a closure that takes two integers and returns their sum:

let addClosure: (Int, Int) -> Int = { (a, b) in
return a + b
}

let result = addClosure(5, 3)
// result will be 8

3. Trailing Closures

Trailing closures allow you to move the closure outside of the parentheses when it’s the last argument of a function, making your code cleaner:

let numbers = [1, 2, 3, 4, 5]
let mappedNumbers = numbers.map { number in
number * 2
}

4. Capturing Values

Closures can capture and store references to variables and constants from their surrounding context. Here’s an example:

func makeIncrementer(incrementAmount: Int) -> () -> Int {
var total = 0
return {
total += incrementAmount
return total
}
}

let incrementByTwo = makeIncrementer(incrementAmount: 2)
print(incrementByTwo()) // Output: 2
print(incrementByTwo()) // Output: 4

5. Escaping Closures

Escaping closures are closures that are stored and called later. They’re marked with the `@escaping` attribute. Here’s an example:

var completionHandlers: [() -> Void] = []

func someFunctionWithEscapingClosure(completionHandler: @escaping () -> Void) {
completionHandlers.append(completionHandler)
}

6. Autoclosures

Autoclosures are a special kind of closure that automatically wraps an expression into a closure. They’re used to delay the evaluation of an expression. Here’s an example:

var customersInLine = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]

print(customersInLine.count) // Output: 5

let customerProvider = { customersInLine.remove(at: 0) }

print(customersInLine.count) // Output: 5

print("Now serving \(customerProvider())!") // Output: Now serving Chris!

print(customersInLine.count) // Output: 4

7. Advanced Closure Techniques

Swift closures support advanced techniques such as capturing lists and shorthand argument names. Here’s an example using shorthand argument names:

let numbers = [1, 2, 3, 4, 5]
let sum = numbers.reduce(0, +) // Equivalent to: numbers.reduce(0, { $0 + $1 })
print(sum) // Output: 15

8. Error Handling with Closures

Closures can be used for error handling, particularly with asynchronous operations. Here’s an example using a closure with a `Result` type:

enum NetworkError: Error {
case failed
}

func fetchData(completion: @escaping (Result<String, NetworkError>) -> Void) {
// Simulate network request
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
completion(.success("Data fetched successfully"))
// Or completion(.failure(.failed))
}
}

fetchData { result in
switch result {
case .success(let data):
print(data) // Output: Data fetched successfully
case .failure(let error):
print(error) // Output: failed
}
}

Closures are a fundamental part of Swift programming, allowing you to write concise and expressive code. By mastering closures and understanding their advanced techniques, you can become a more proficient Swift developer. Experiment with closures in your own projects and explore the possibilities they offer.

Buy me a coffee: https://www.buymeacoffee.com/ShahriarDev

Linkedin: https://www.linkedin.com/in/shahriar-hossain-dev/

--

--