How to Use Closures in Swift

Use closures in Swift when you need inline reusable behavior, callbacks, sorting logic, async completions, or UI actions. Closures are one of the most common building blocks in Swift and SwiftUI.

What you’ll build or solve

You’ll learn how to use closures in Swift with parameters, return values, shorthand syntax, and trailing closures. You’ll also know when named functions are clearer.

When this approach works best

This approach is the right choice when the logic is short and closely tied to where it is used.

Common real-world scenarios include:

  • Sorting arrays
  • Button actions
  • Network callbacks
  • Completion handlers
  • Collection transforms

This is a bad idea when the logic is large enough to deserve a named reusable function.

Prerequisites

You only need:

  • Basic Swift functions
  • Arrays
  • Familiarity with parameters and returns

Step-by-step instructions

Step 1: Create a basic closure

A closure can be stored in a variable.

let greet = { (name: String) -> String in
    "Hello, \(name)"
}

Call it like a function.

let message = greet("Srdan")

This is perfect for local reusable logic.

Step 2: Use closures with array methods

A common real-world use is sorting and mapping.

let scores = [95, 88, 76]

let sorted = scores.sorted { a, b in
    a > b
}

This keeps related logic close to the collection operation.

Step 3: Use trailing closure syntax

Swift prefers trailing closures when the closure is the last argument.

let doubled = scores.map { score in
    score * 2
}

This is the cleanest modern style.

Step 4: Use shorthand argument names

Short closures can become even smaller.

let reversed = scores.sorted { $0 > $1 }

This is ideal for simple one-line transforms.

What to look for:

  • Closures store inline behavior
  • Great for callbacks and transforms
  • Trailing syntax improves readability
  • Use shorthand for simple logic
  • Use named functions for larger logic

Examples you can copy

Sort descending

scores.sorted { $0 > $1 }

Map values

scores.map { $0 * 2 }

Button action

Button("Save") {
    print("Saved")
}

Common mistakes and how to fix them

Mistake 1: Overusing shorthand for complex logic

What the reader might do:

Use $0, $1, $2 in long blocks.

Why it breaks: readability drops fast.

Corrected approach:

Use named parameters.

Mistake 2: Using closures for large reusable logic

What the reader might do:

Write 20 lines inline.

Why it breaks: the call site becomes noisy.

Corrected approach:

Extract a named function.

Mistake 3: Capturing self strongly in async closures

What the reader might do:

Use self directly in long-lived callbacks.

Why it breaks: retain cycles can happen.

Corrected approach:

Use [weak self] when needed.

Troubleshooting

If the closure syntax feels noisy, switch to trailing closures.

If shorthand is hard to read, use named parameters.

If memory leaks appear, inspect captured references.

If the closure grows large, extract a function.

Quick recap

  • Closures store inline behavior
  • Great for sorting, mapping, and callbacks
  • Prefer trailing closure syntax
  • Use shorthand only for simple logic
  • Extract larger logic into named functions