Skip to main content

Swift Trailing Closures

Introduction

In Swift, closures are self-contained blocks of functionality that can be passed around and used in your code. When a function's last parameter is a closure, Swift provides a special syntax called trailing closure syntax that makes your code cleaner and more readable.

Trailing closures allow you to write the closure outside of the function's parentheses, making complex function calls with closure arguments more elegant and easier to understand, especially when the closure contains multiple lines of code.

Understanding Trailing Closures

Basic Syntax

Here's the standard way to call a function that takes a closure as a parameter:

swift
// Standard closure syntax
function(parameterOne: value) {
// closure body
}

And here's how it looks with trailing closure syntax:

swift
// Trailing closure syntax
function(parameterOne: value) {
// closure body
}

Notice that the syntax looks identical! That's because when the closure is the final parameter, you're allowed to omit the parameter label and move the closure outside the parentheses.

When to Use Trailing Closures

You can use trailing closure syntax when:

  1. A function's last parameter is a closure
  2. You're calling the function with that closure parameter

Let's see this in action with a simple example:

swift
// A function that takes two parameters, the second one being a closure
func greet(person: String, message: () -> Void) {
print("Hello, \(person)!")
message()
}

// Calling the function using standard syntax
greet(person: "John", message: {
print("Have a great day!")
})

// Calling the function using trailing closure syntax
greet(person: "John") {
print("Have a great day!")
}

Output for both calls:

Hello, John!
Have a great day!

As you can see, the trailing closure syntax makes the function call cleaner and more readable.

Multiple Trailing Closures

Starting with Swift 5.3, you can use multiple trailing closures in a function call. The first trailing closure still uses the same syntax we've seen, but additional closures use a labeled format:

swift
func processPicture(with filter: (UIImage) -> UIImage, completion: (UIImage) -> Void, onFailure: (Error) -> Void) {
// Function implementation
}

// Using multiple trailing closures
processPicture(with: someFilter) { filteredImage in
// Handle the filtered image
} onFailure: { error in
// Handle any errors
}

Real-World Examples

Example 1: Array Sorting

One common use of trailing closures is with array sorting:

swift
let names = ["Chris", "Alex", "Barry", "Diana"]

// Standard closure syntax
let sortedNames1 = names.sorted(by: { (s1: String, s2: String) -> Bool in
return s1 < s2
})

// Trailing closure syntax
let sortedNames2 = names.sorted { s1, s2 in
return s1 < s2
}

// Even shorter with implicit return
let sortedNames3 = names.sorted { $0 < $1 }

print(sortedNames3)

Output:

["Alex", "Barry", "Chris", "Diana"]

Example 2: Animations in UIKit

If you're developing iOS apps, you'll often use trailing closures with animations:

swift
// Standard closure syntax
UIView.animate(withDuration: 0.3, animations: {
view.alpha = 0
})

// Trailing closure syntax
UIView.animate(withDuration: 0.3) {
view.alpha = 0
}

Example 3: Network Requests

Trailing closures are commonly used in completion handlers for network requests:

swift
// Function that fetches data from a URL
func fetchData(from url: URL, completion: (Data?, Error?) -> Void) {
// Implementation details...
}

// Using trailing closure syntax
fetchData(from: someURL) { (data, error) in
if let error = error {
print("Error: \(error.localizedDescription)")
return
}

guard let data = data else {
print("No data received")
return
}

// Process the data
print("Downloaded \(data.count) bytes")
}

Common Higher-Order Functions with Trailing Closures

Swift's standard library includes several higher-order functions that work great with trailing closures:

Map

The map function transforms each element in a collection:

swift
let numbers = [1, 2, 3, 4, 5]

// Standard syntax
let doubled1 = numbers.map({ (number) -> Int in
return number * 2
})

// Trailing closure syntax
let doubled2 = numbers.map { number in
return number * 2
}

// Shortest form with implicit return and shorthand argument
let doubled3 = numbers.map { $0 * 2 }

print(doubled3) // [2, 4, 6, 8, 10]

Filter

The filter function returns elements that meet a condition:

swift
let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

// Using trailing closure
let evenNumbers = numbers.filter { $0 % 2 == 0 }

print(evenNumbers) // [2, 4, 6, 8, 10]

Reduce

The reduce function combines all items in a collection:

swift
let numbers = [1, 2, 3, 4, 5]

// Using trailing closure
let sum = numbers.reduce(0) { $0 + $1 }

print(sum) // 15

Best Practices for Using Trailing Closures

  1. Use trailing closures for readability - Especially for longer closures or when the closure is the primary focus of the function call

  2. Consider nesting - Be careful with nested trailing closures as they can reduce readability:

swift
// This can get confusing
fetchData(from: url) { data in
processImage(data) { image in
displayImage(image) {
// More nested code...
}
}
}
  1. Be consistent - Maintain a consistent style throughout your codebase

Summary

Trailing closures are a syntactic sugar feature in Swift that enhances code readability when working with functions whose last parameter is a closure. This feature is especially valuable when:

  • The closure contains multiple lines of code
  • The closure is more important than other parameters
  • You're working with higher-order functions like map, filter, and reduce

By enabling you to move the closure outside of the function's parentheses, trailing closures make Swift code cleaner, more readable, and more natural to write and understand.

Exercises

  1. Convert the following function call to use trailing closure syntax:

    swift
    let numbers = [1, 2, 3, 4, 5]
    let squaredNumbers = numbers.map({ num in return num * num })
  2. Write a function called processString that takes a string and a closure that transforms the string. Then call this function using trailing closure syntax.

  3. Use trailing closure syntax with the forEach method to print each element of an array on a new line.

Additional Resources



If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)