Swift closures, covering how they let you group bits of executable code inline
  • July 24, 2025

 

1. What Is a Closure?

A closure is a self-contained block of functionality you can pass around and use in your code. In Swift, closures can:

  • Capture and store references to any constants and variables from the surrounding context.
  • Be written inline, without needing a named function.
  • Be assigned to variables, passed into or returned from functions, and used wherever a function type is expected.

Closures come in three flavors:

  1. Global functions are closures that have a name and don’t capture values.
  2. Nested functions are named closures that capture values from their enclosing function.
  3. Closure expressions are unnamed, lightweight syntax for writing inline closures.

2. The sorted(by:) Method

A 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.


3. Closure Expression Syntax

Closure expressions let you write inline closures without the overhead of a named function. The full form is:

{ (parameters) -> ReturnType in
    statements
}
  • Parameters: a comma-separated list, just like a function.
  • Return type: written after ->.
  • in: separates the signature from the body.
  • Statements: the closure’s code.

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

4. Inferring Types From Context

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.


5. Shorthand Argument Names

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.


6. Implicit Returns from Single-Expression 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 }

7. Trailing Closure Syntax

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 }

8. Capturing Values

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.


9. When to Use a Closure vs. a Function

  • Use a named function when you have a piece of logic shared in multiple places or it’s complex enough to deserve its own declaration.
  • Use an inline closure when the logic is short, used only once (or passed directly), and it improves readability by keeping it close to where it’s used.

Quick Reference

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!

YOU MIGHT ALSO LIKE...
🧭 NavigationKit

NavigationKit is a lightweight library which makes SwiftUI navigation super easy to use. 💻 Installation 📦 Swift Package Manager Using Swift Package Manager, add ...

swiftui-navigation-stack

An alternative SwiftUI NavigationView implementing classic stack-based navigation giving also some more control on animations and programmatic navigation. NavigationStack Installation ...

Stinsen

Simple, powerful and elegant implementation of the Coordinator pattern in SwiftUI. Stinsen is written using 100% SwiftUI which makes it ...

SwiftUI Router

With SwiftUI Router you can power your SwiftUI app with path-based routing. By utilizing a path-based system, navigation in your app becomes ...

FlowStacks

This package takes SwiftUI's familiar and powerful NavigationStack API and gives it superpowers, allowing you to use the same API not just ...