- July 29, 2025
- Mins Read
A closure in Swift is a self-contained block of functionality that you can pass around and use in your code. Closures can:
Swift uses closures everywhere:
URLSession.shared.dataTask { … }
)map
, filter
, reduce
)Because they’re so flexible and lightweight, closures let you express complex behavior inline, without the ceremony of defining a named function.
A closure’s full form looks a lot like a function:
{ (parameters) -> ReturnType in
// body
}
{ … }
denote the closure.-> ReturnType
tells what it returns.in
keyword separates the signature from the body.let sayHello = {
print("Hello, world!")
}
sayHello() // prints "Hello, world!"
Unlike function declarations, closures are expressions, so their parameter list and return type must live entirely inside the {}
. There’s no separate signature line:
// Function declaration
func add(a: Int, b: Int) -> Int { return a + b }
// Closure expression
let addClosure = { (a: Int, b: Int) -> Int in
return a + b
}
Putting everything inside {}
keeps closures self-contained and emphasizes that you’re creating a first-class value, not defining a top-level function.
Swift can infer parameter types and return types, and even let you use shorthand argument names. That’s why you often see:
// Full form
let sumFull = { (a: Int, b: Int) -> Int in
return a + b
}
// Inferred form
let sumInferred: (Int, Int) -> Int = { a, b in
a + b // implicit return
}
// Shorthand argument names
let sumShorthand: (Int, Int) -> Int = { $0 + $1 }
If your closure takes no parameters but returns something, you still use the -> Type in
syntax and a return
(or implicit return for single expressions):
let randomBool: () -> Bool = {
return Bool.random()
}
// Or shorthand (single expression):
let randomShorthand: () -> Bool = {
Bool.random()
}
// Call it
let value = randomBool()
Below are small “tests” you can paste into a Swift playground or project to verify your understanding of closures.
// Test 1: No parameters, no return
let greet: () -> Void = {
print("Test 1 Passed: Hello from closure!")
}
greet()
Expected output:
Test 1 Passed: Hello from closure!
// Test 2: Parameters, no return
let repeatPrint: (String, Int) -> Void = { message, count in
for _ in 1...count {
print(message)
}
}
print("Test 2 Output:")
repeatPrint("Swift", 3)
Expected output:
Test 2 Output:
Swift
Swift
Swift
// Test 3a: Return an Int
let multiply: (Int, Int) -> Int = { a, b in
return a * b
}
assert(multiply(4, 5) == 20)
print("Test 3a Passed: 4 * 5 == \(multiply(4, 5))")
// Test 3b: No parameters, return Bool
let isEven: (Int) -> Bool = { $0 % 2 == 0 }
assert(isEven(42) == true)
print("Test 3b Passed: 42 is even? \(isEven(42))")
{ (params) -> ReturnType in … }
syntax entirely inside braces.{ $0 + $1 }
).() -> Type
and use return
(or implicit for single expressions).1. Taking Action When a Property Changes: Property Observers Swift lets you observe and respond to changes in a property’s ...
1. Creating Your Own Structs In Swift, a struct is a value type that you define with the struct keyword. ...
1. Trailing Closure Syntax When the last parameter to a function is a closure, you can write that closure after ...
1. Providing Default Values for Function Parameters (Deep Dive) 1.1 Syntax and Ordering Declaration You assign a default right in ...