Closures in Depth
Closures are blocks of code you can pass around like values. This lesson covers full and shorthand syntax, trailing closures , capturing values, and the difference between escaping and non-escaping closures — the foundation of callbacks and functional Swift.
Learn Closures in Depth in our free Swift course — a beginner-friendly interactive lesson with worked examples, a practice exercise and a quick reference.
Part of the free Swift course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.
What You'll Learn in This Lesson
- • Write closures with the { params -> Type in … } form
- • Use trailing closure syntax for cleaner calls
- • Shrink closures with shorthand $0 , $1 and inferred returns
- • Understand how closures capture surrounding values
- • Tell escaping from non-escaping closures
- • Know when self must be written explicitly
1️⃣ Closure Syntax
The full closure form is { (params) -> ReturnType in body } . The in keyword separates the signature from the body. A closure can take no parameters and return nothing — its type is then () -> Void .
2️⃣ Trailing Closures
When a closure is the last argument to a function, you can write it after the parentheses. This trailing-closure style is everywhere in Swift and SwiftUI because it reads so cleanly.
3️⃣ Shorthand: $0, Inferred Types & Returns
Swift lets you drop a lot of ceremony. Parameter types can be inferred, a single-expression body needs no return , and $0 / $1 name the arguments positionally.
4️⃣ Capturing Values (and Escaping)
A closure captures variables from the scope where it's defined and keeps them alive. A closure that's stored and called after the function returns is escaping — you mark its parameter @escaping , and using self inside it must be explicit.
Your turn. Fill in the blanks to use shorthand arguments and the in keyword.
Pro Tips
- 💡 Use trailing closures for the final argument — they read best for multi-line bodies.
- 💡 Reach for $0 sparingly. Shorthand is great for tiny closures; name parameters when logic grows.
- 💡 Mark stored callbacks @escaping . Completion handlers almost always escape.
- 💡 Watch retain cycles. In escaping closures capturing self, consider [weak self] .
Common Errors (and the fix)
- "Escaping closure captures non-escaping parameter" — you stored a closure that wasn't marked @escaping . Add the attribute.
- "Reference to property 'x' in closure requires explicit use of 'self'" — in an escaping closure, write self.x .
- "Contextual closure type expects N arguments" — your closure's parameter count doesn't match. Match the expected signature.
- "Missing return in closure" — a multi-statement closure needs an explicit return .
📋 Quick Reference
Task
Code
Note
Full closure
{ (a: Int) -> Int in a*2 }
signature + in
Shorthand
{ $0 < $1 }
positional args
Trailing
run { … }
after the parens
No params
() -> Void
empty closure type
Escaping
@escaping () -> Void
stored for later
Frequently Asked Questions
Mini-Challenge: applyTwice
No blanks this time — just a brief and an outline. Build it, run it, and check your output against the example in the comments.
🎉 Lesson Complete!
- ✅ Closures: { params -> Type in body }
- ✅ Trailing closures move the last closure outside the parentheses
- ✅ Shorthand $0 / $1 and inferred returns trim the noise
- ✅ Closures capture and keep surrounding values alive
- ✅ @escaping closures run after the function returns
- ✅ Next lesson: map, filter & reduce
Practice quiz
What is a closure in Swift?
- A class that can't be subclassed
- A type of optional
- A self-contained block of code that can be passed around
- A protocol requirement
Answer: A self-contained block of code that can be passed around. A closure is a self-contained block of functionality that can be stored, passed, and called later.
What keyword separates a closure's parameters from its body?
- in
- return
- do
- then
Answer: in. The 'in' keyword separates the parameter/return list from the closure body.
What is a trailing closure?
- A closure stored in a variable
- A closure that returns nothing
- A closure with no parameters
- A closure written after the call's parentheses when it's the last argument
Answer: A closure written after the call's parentheses when it's the last argument. When a closure is the final argument, you can move it outside the parentheses as a trailing closure.
What does the shorthand argument $0 refer to?
- The return value
- The first parameter of the closure
- The closure itself
- An optional binding
Answer: The first parameter of the closure. $0 is shorthand for the first parameter, $1 the second, and so on.
What does it mean to 'capture' a value in a closure?
- To keep a reference to a variable from the surrounding scope
- To copy it into a struct
- To make it constant
- To delete it after use
Answer: To keep a reference to a variable from the surrounding scope. A closure captures variables/constants from its surrounding context and can keep using them.
What is an escaping closure?
- A closure that throws errors
- A closure that can't capture values
- A closure stored and called AFTER the function returns
- A closure with no body
Answer: A closure stored and called AFTER the function returns. An @escaping closure outlives the function call — it's stored and invoked later, e.g. in a completion handler.
By default, are closure parameters to a function escaping or non-escaping?
- Escaping
- Non-escaping
- Always escaping for async
- Neither — it must be specified
Answer: Non-escaping. Closure parameters are non-escaping by default; you opt in to escaping with @escaping.
When can you omit a closure's 'return' keyword?
- Never
- Only for trailing closures
- Only when it returns Void
- When the closure body is a single expression
Answer: When the closure body is a single expression. For a single-expression closure, Swift infers the return, so you can omit 'return'.
Which is a valid empty closure type that takes no parameters and returns nothing?
- Void -> Int
- () -> ()
- (Int)
- { }
Answer: () -> (). () -> () (or () -> Void) is a closure taking no arguments and returning nothing.
Why might @escaping closures require you to write self explicitly?
- For performance
- Because self is optional
- To make capturing of self visible and avoid accidental retain cycles
- It is never required
Answer: To make capturing of self visible and avoid accidental retain cycles. Escaping closures that use self require explicit self, making the strong capture (and possible retain cycle) obvious.
Continue this course
- Previous: Methods & Initializers
- Next: map, filter & reduce