In This Article
- What Is a Closure?
- Closure Syntax and Shorthand
- Trailing Closure Syntax
- Capturing Values From Surrounding Scope
- Custom Sorting With Closures
- forEach: Looping Functionally
- filter: Keeping What Matters
- map: Transforming Every Element
- compactMap and flatMap
- reduce: Collapsing to a Single Value
- Chaining Operations
- Lazy Collections
- Exercises
- Key Points
In the previous chapter, you used for-in loops to iterate over arrays and dictionaries. Closures unlock a more powerful, expressive way to work with collections — transforming, filtering, and reducing data in just a few lines. This chapter introduces closures and the functional operations built on top of them.
What Is a Closure?
A closure is a function without a name. You can assign it to a variable, pass it to a function, and return it from a function — just like any other value. Closures are called "closures" because they can close over (capture) variables and constants from the surrounding scope.
The closure looks like a function body wrapped in braces. The keyword in separates the parameter list and return type from the body. The type of multiplyClosure is (Int, Int) -> Int — the same type as the function version.
Closure Syntax and Shorthand
Swift provides progressively shorter ways to write closures. Starting from the full form:
The $0, $1 shorthand refers to the first, second (etc.) parameters by position. Use this only for short closures where the meaning is obvious.
Trailing Closure Syntax
When a closure is the last parameter of a function call, you can pull it out of the parentheses. This is called trailing closure syntax and is extremely common in Swift:
You'll see trailing closure syntax everywhere in Swift — sorting, filtering, animations, SwiftUI views. It's one of the features that makes Swift code read so naturally.
Capturing Values From Surrounding Scope
The defining feature of closures: they can access and modify variables from their enclosing scope.
The closure captures counter — it holds a reference to the same variable, not a copy. Changes inside the closure are visible outside, and vice versa.
This enables powerful patterns like closure factories:
Each call to makeCounter() creates a new, independent count variable. Each returned closure captures its own copy.
Custom Sorting With Closures
The closure tells sorted how to compare two elements. Return true if the first should come before the second.
forEach: Looping Functionally
forEach is an alternative to for-in loops. Use whichever reads better — for-in is more common for complex logic, forEach for simple one-liners.
filter: Keeping What Matters
filter returns a new array containing only elements that pass a test:
The closure takes each element and returns true to keep it or false to exclude it. The original array is never modified.
map: Transforming Every Element
map applies a transformation to every element and returns a new array of results:
map preserves the count and order. Each element is transformed independently.
compactMap and flatMap
compactMap: map + remove nils
compactMap works like map but automatically filters out nil results. Perfect for parsing or converting data where some values might be invalid.
flatMap: flatten nested collections
flatMap takes a closure that returns a collection, then concatenates all the results into a single flat array.
reduce: Collapsing to a Single Value
reduce combines all elements into a single value using an accumulator:
The first argument (0) is the initial value. The closure receives the running total ($0) and the current element ($1), returning the new running total.
reduce works on dictionaries too:
Chaining Operations
The real power emerges when you chain these operations together:
Three operations, three lines, no loops, no temporary variables. Each operation returns a new collection that feeds into the next.
Lazy Collections
What if your collection is huge (or infinite)? Adding .lazy tells Swift to evaluate elements on demand instead of all at once:
Without .lazy, Swift would try to filter an infinite range — which would never finish. With .lazy, it only evaluates elements as needed, stopping after finding 10 primes.
Use .lazy when (1) the collection is very large, (2) computation per element is expensive, or (3) you only need a subset of results. For small collections, regular (eager) evaluation is fine and simpler to debug.
Exercises
Try These in Your Playground
- Create an array of names. Use
reduceto concatenate them into a single string separated by commas. - Using the same array, chain
filter(names longer than 4 characters) andreduce(concatenate) together. - Create a dictionary of names to ages. Use
filterto get only people under 18, then usemapto extract just the names into an array. - Write a function
repeatTask(times: Int, task: () -> Void)that runs a closure a given number of times. Use it to print a message 5 times. - Use
compactMapto convert["1", "abc", "3", "def", "5"]into an[Int]. - Challenge: Use
reduceto find the sum of the first 10 Fibonacci numbers without using a loop.
Key Points
What You Learned
- Closures are unnamed functions that can be assigned to variables and passed as arguments
- Shorthand syntax: omit types, omit
return, use$0/$1for parameters - Trailing closure syntax moves the last closure argument outside the parentheses
- Closures capture variables from their enclosing scope by reference
sorted { }lets you define custom sort ordersfilterkeeps elements matching a condition;first(where:)finds the first matchmaptransforms every element;compactMapalso removes nils;flatMapflattens nested collectionsreducecollapses a collection into a single value using an accumulator- Chain
filter,map, andreducefor powerful data transformations in a few lines .lazydefers evaluation for large or infinite collections
This completes Section II of the Swift fundamentals. In the next chapter, we begin Section III: Building Your Own Types, starting with Structs — Swift's primary tool for modeling data.
Watch the video lessons
Our Swift Fundamentals course covers closures, functional patterns, and real-world collection operations in 96 video lessons.
Watch Swift Videos