Closures are self-contained blocks of code in Swift. They can be passed around and used in any iOS code. They work like functions, but can capture and store references to variables and constants from their surrounding context, this is known as closing over those variables.
Closure Syntax
{ (parameters) -> returnType in // code
}
Example of a Simple Closure
let greet = { print("Hello, Swift!")
}
greet() // Output: Hello, Swift!
Closure with Parameters and Return Type
let add: (Int, Int) -> Int = { (a, b) in return a + b
}
let result = add(3, 5) // 8
Closure as Function Parameter (e.g., Completion Handler)
func performTask(completion: () -> Void) { print("Task started") completion() print("Task ended")
}
performTask { print("Task in progress...")
}
Trailing Closure Syntax
If the last parameter of a function is a closure, you can use trailing closure syntax:
performTask { print("Using trailing closure")
}
Capturing Values
Closures can capture and store variables from the surrounding scope:
func makeCounter() -> () -> Int { var count = 0 return { count += 1 return count }
}
let counter = makeCounter()
print(counter()) // 1
print(counter()) // 2
Memory Management with Closures (Capture Lists)
Closures capture strongly by default, which can lead to retain cycles especially in classes.
Retain Cycle Example
class MyClass { var name = "Swift" var closure: (() -> Void)? func setup() { closure = { print(self.name) } }
}
Fix with [weak self] or [unowned self]
closure = { [weak self] in print(self?.name ?? "nil")
}
Shorthand Argument Names
Swift allows you to omit parameter names and use $0, $1, etc.
let numbers = [1, 2, 3]
let doubled = numbers.map { $0 * 2 } // [2, 4, 6]
Conclusion
Closures make Swift code more flexible by allowing you to pass around blocks of logic and capture values from context. When you hire iOS developers who understand how closures work, including memory management and capture lists, your app becomes cleaner, faster, and easier to maintain.