Chapter 75: Swift Closures
1. What is a closure? (the most honest, intuitive explanation)
A closure is a function without a name that can:
- capture variables from the surrounding scope (even after that scope ends)
- be passed around like any other value
- be stored in variables, arrays, dictionaries…
- be called later
In simple words:
A closure is an anonymous function that remembers the environment where it was created.
Think of a closure as a small portable worker that:
- Knows its instructions (the code inside {})
- Remembers who hired it and what tools it was given (captured variables)
- Can be sent to another department (passed as parameter)
- Can be called much later (even after the original room is closed)
2. The basic syntax — every form you will actually see
2.1 Simplest closure — no parameters, no return
|
0 1 2 3 4 5 6 7 8 9 10 |
let sayHello = { print("Namaste, Hyderabad! 🌶️") } sayHello() // Namaste, Hyderabad! 🌶️ |
2.2 Closure with parameters & return value
|
0 1 2 3 4 5 6 7 8 9 10 |
let add = { (a: Int, b: Int) -> Int in return a + b } print(add(7, 8)) // 15 |
Shorthand syntax (very common & clean):
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 |
let multiply = { $0 * $1 } // implicit parameters $0, $1 print(multiply(6, 7)) // 42 let greet = { name in print("Namaste, \(name)! 👋") } greet("Rahul") // Namaste, Rahul! 👋 |
2.3 Trailing closure syntax (the most beautiful & most used style)
When the last parameter of a function is a closure, Swift lets you write it outside the parentheses — very clean.
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
// Without trailing closure array.forEach( { item in print(item) } ) // With trailing closure — much more readable array.forEach { item in print(item) } // Even shorter with implicit $0 array.forEach { print($0) } |
You will see trailing closures everywhere in SwiftUI, Combine, animations, networking, etc.
3. Capturing values — the real superpower of closures
Closures can remember variables from the surrounding scope — even after that scope disappears.
This is called capturing or closing over variables.
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
func makeCounter() -> () -> Int { var count = 0 let increment = { count += 1 // ← captures & modifies count return count } return increment } let counter1 = makeCounter() let counter2 = makeCounter() print(counter1()) // 1 print(counter1()) // 2 print(counter1()) // 3 print(counter2()) // 1 ← separate counter |
Real-life analogy:
Think of a counter function as a ticket machine at a parking lot. Each time you call makeCounter(), you get a new independent machine — each remembers its own ticket count.
This is why closures are so powerful for:
- callbacks
- completion handlers
- stateful operations
- event handlers
4. Real-life examples — closures you will actually write every day
Example 1 – Completion handler (networking, animations, alerts)
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
func fetchUser(id: Int, completion: @escaping (String?, Error?) -> Void) { // pretend network delay DispatchQueue.main.asyncAfter(deadline: .now() + 1.5) { if id == 1 { completion("Rahul", nil) } else { completion(nil, NSError(domain: "User", code: 404)) } } } // Usage — trailing closure style (very common) fetchUser(id: 1) { name, error in if let name { print("User: \(name)") } else if let error { print("Error: \(error.localizedDescription)") } } |
Example 2 – Sorting with custom comparator (very frequent)
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
struct Product { let name: String let price: Double } let products = [ Product(name: "Earbuds Pro", price: 24999), Product(name: "Phone Case", price: 999), Product(name: "Charger", price: 2499), Product(name: "Earbuds", price: 9999) ] // Sort by price ascending, then name alphabetical let sorted = products.sorted { a, b in if a.price != b.price { return a.price < b.price } return a.name < b.name } // Trailing closure style (very clean) products.forEach { print("₹\($0.price) – \($0.name)") } |
Example 3 – SwiftUI button action (very common)
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
Button("Tap me") { print("Button tapped!") } // with parameters Button(action: { print("Complex action") saveData() showConfirmation() }) { Text("Save") } |
Example 4 – Animation completion (very typical in SwiftUI/UIKit)
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 |
UIView.animate(withDuration: 0.5) { view.alpha = 0 } completion: { finished in if finished { view.removeFromSuperview() print("Animation completed") } } |
5. Very Common Beginner Mistakes & Correct Habits
| Mistake | Wrong / Dangerous code | Correct / Better habit | Why? |
|---|---|---|---|
| Forgetting @escaping on completion handlers | func fetch(completion: (String) -> Void) | func fetch(completion: @escaping (String) -> Void) | Without @escaping you get compile error when storing the closure |
| Strong reference cycles (memory leak) | self.button.action = { self.doSomething() } | Use [weak self] or [unowned self] | Retain cycle — view controller never deallocates |
| Using var inside closure when not needed | var count = 0; closure = { count += 1 } | let count = 0 if not changing | let is safer |
| Long closure body without trailing syntax | array.map({ item in … }) | array.map { item in … } | Trailing closure is much more readable |
| Mutating captured variable without var | let counter = 0; increment = { counter += 1 } | var counter = 0 | Compile error — captured let cannot be mutated |
6. Quick Summary — Closure patterns you will use every day
| Goal | Most idiomatic code | Notes / Tip |
|---|---|---|
| Simple completion handler | fetch { result in … } | Trailing closure style |
| Capture self safely | { [weak self] in self?.doSomething() } | Prevents retain cycles |
| Sort with custom logic | sorted { $0.price < $1.price } | Very common |
| Animation / alert completion | animate { … } completion: { finished in … } | Trailing closure is standard |
| Map / filter / reduce chains | array.filter { $0 > 10 }.map { $0 * 2 } | Functional style — very idiomatic |
7. Small Practice – Try these
- Create a function greetPeople(_ names: [String], greeting: String = “Namaste”) that uses forEach to print “Namaste, Rahul!” for each name.
- Write a closure that calculates discount: let applyDiscount = { (price: Double, percent: Double) -> Double in price * (1 – percent/100) }
- Create a retry function using closure: func retry(times: Int, delay: Double, operation: @escaping () -> Bool)
Paste your code here if you want feedback or want to see even cleaner versions!
What would you like to explore next?
- @escaping closures in depth
- Capturing values — strong, weak, unowned
- Trailing closures in SwiftUI & Combine
- Closure as function parameter vs function type
- Escaping vs non-escaping closures
- Or move to another topic (optionals, arrays, switch, protocols…)
Just tell me — we’ll continue in the same clear, patient, detailed style 😊
