- July 26, 2025
- Mins Read
A closure is a self-contained block of functionality you can pass around and use in your code. In Swift, closures can:
Closures come in three flavors:
sorted(by:)
MethodA common use of closures is with collection methods like sorted(by:)
, map(_:)
, filter(_:)
, and reduce(_:_:)
. For example, given an array of strings:
let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
You can sort it in descending alphabetical order by passing a closure that compares two elements:
let descending = names.sorted { a, b in
return a > b
}
print(descending) // ["Ewa", "Daniella", "Chris", "Barry", "Alex"]
Here, sorted(by:)
expects a closure of type (String, String) -> Bool
, which returns true
if the first argument should come before the second.
Closure expressions let you write inline closures without the overhead of a named function. The full form is:
{ (parameters) -> ReturnType in
statements
}
->
.in
: separates the signature from the body.Example: a closure that adds two integers:
let add: (Int, Int) -> Int = { (x: Int, y: Int) -> Int in
return x + y
}
add(3, 5) // 8
Swift’s type inference lets you drop much of that boilerplate when the compiler already knows the expected types:
// sorted(by:) expects (String, String) -> Bool, so we can omit types:
let desc2 = names.sorted { (a, b) in
return a > b
}
Because sorted(by:)
’s signature is (Element, Element) -> Bool
, the compiler infers that a
and b
are String
, and the return type is Bool
.
Swift provides shorthand argument names—$0
, $1
, $2
, …—so you can omit the parameter list entirely:
// Compare first arg ($0) to second ($1):
let desc3 = names.sorted { $0 > $1 }
Here:
$0
refers to the first parameter,$1
to the second.This is ideal for very simple closures.
If the closure body is a single expression, Swift lets you omit the return
keyword:
// Full: { a, b in return a > b }
// Implicit return:
let desc4 = names.sorted { a, b in a > b }
Combine this with type inference and shorthand names, and you get the most concise form:
let desc5 = names.sorted { $0 > $1 }
When the final argument to a function is a closure, you can write it after the function call’s parentheses:
// Without trailing closure:
let desc6 = names.sorted(by: { $0 > $1 })
// With trailing closure:
let desc7 = names.sorted { $0 > $1 }
If the closure is the only argument, you can even drop the empty parentheses entirely:
let desc8 = names.sorted { $0 > $1 }
Closures can capture constants and variables from their surrounding context:
func makeIncrementer(by amount: Int) -> () -> Int {
var total = 0
return {
total += amount // captures both `total` and `amount`
return total
}
}
let incByTen = makeIncrementer(by: 10)
incByTen() // 10
incByTen() // 20
Each call to makeIncrementer
creates its own total
variable that the returned closure closes over.
Feature | Full Syntax | Shorthand |
---|---|---|
Type annotation | { (x: Int, y: Int) -> Int in ... } |
Compiler infers from context |
Argument list | (x, y) in ... |
$0, $1 |
Return keyword | return expr |
Omit for single-expression bodies |
Trailing closure | .method(arg: val, { … }) |
.method { … } |
With these tools—closure expressions, shorthand names, implicit returns, and trailing syntax—you can write concise, powerful inline behaviors in Swift. Play around with map
, filter
, reduce
, compactMap
, and flatMap
next to see closures in action across the standard library!
NavigationKit is a lightweight library which makes SwiftUI navigation super easy to use. 💻 Installation 📦 Swift Package Manager Using Swift Package Manager, add ...
An alternative SwiftUI NavigationView implementing classic stack-based navigation giving also some more control on animations and programmatic navigation. NavigationStack Installation ...
With SwiftUI Router you can power your SwiftUI app with path-based routing. By utilizing a path-based system, navigation in your app becomes ...
This package takes SwiftUI's familiar and powerful NavigationStack API and gives it superpowers, allowing you to use the same API not just ...