guard & defer
Two small keywords that make Swift code dramatically cleaner: guard exits early when something is wrong so your main logic stays flat, and defer guarantees cleanup runs no matter how a function ends.
Learn guard & defer 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
1️⃣ guard — Exit Early, Stay Flat
A guard states what must be true to continue. If the condition fails, the else block runs and must leave the current scope (usually return ). The big win: any value unwrapped by guard let stays in scope below the guard, so your main logic never gets buried in nested if blocks.
2️⃣ Validating Several Conditions Up Front
Stacking guards is the idiomatic way to validate inputs at the top of a function. Each one peels off a failure case, and every binding remains available afterwards.
Your turn. Fill in the blank to make this function exit early when the name is missing.
3️⃣ defer — Cleanup That Always Runs
defer registers a block to run as the current scope exits — on a normal return, an early return, or a thrown error. Multiple defer blocks unwind in reverse order (last declared runs first), which mirrors how you'd tear resources down.
Now you try. Predict the order, then run it to confirm.
📋 Quick Reference
No blanks this time — just a brief and an outline. Build it, run it, and confirm the deferred log prints on every exit path.
Practice quiz
What is the main purpose of a guard statement?
- To loop over a collection
- To define a new type
- To exit the current scope early when a condition is not met
- To pause an async task
Answer: To exit the current scope early when a condition is not met. guard checks a condition and, if it fails, runs its else block which must leave the current scope.
A guard statement's else block must do what?
- Transfer control out of the current scope (return, break, continue, or throw)
- Return a value
- Print an error
- Call another guard
Answer: Transfer control out of the current scope (return, break, continue, or throw). The else block of guard must exit the scope — via return, break, continue, throw, or fatalError.
Where is a value bound by 'guard let name = optional' available?
- Only inside the guard's else block
- Only on the line with the guard
- Nowhere — it is discarded
- For the rest of the enclosing scope after the guard
Answer: For the rest of the enclosing scope after the guard. Unlike if let, a guard let binding stays in scope after the guard, which is its key advantage.
When does code inside a defer block run?
- Immediately when reached
- When the current scope is about to exit
- Only if an error is thrown
- At program startup
Answer: When the current scope is about to exit. defer schedules its block to run as the enclosing scope exits, however it exits.
If a function has two defer blocks, in what order do they run?
- In reverse order — last declared runs first
- First declared runs first
- Both run at the same time
- Only the first one runs
Answer: In reverse order — last declared runs first. Multiple defer blocks execute in reverse (LIFO) order as the scope exits.
Does a defer block still run if the function returns early via guard?
- No, defer is skipped on early return
- Only if the return is at the end
- Yes, defer runs on any exit from its scope after it was reached
- Only inside loops
Answer: Yes, defer runs on any exit from its scope after it was reached. defer runs no matter how the scope exits — normal return, early return, or thrown error.
Compared to if let, why is guard let often preferred for validation?
- It is faster at runtime
- It keeps the happy path unindented and the unwrapped value in scope
- It cannot fail
- It works only with strings
Answer: It keeps the happy path unindented and the unwrapped value in scope. guard let avoids the 'pyramid of doom' — the main logic stays at the top level and the binding persists.
What happens if a defer block is declared but its line is never reached before the scope exits?
- It still runs
- It causes a crash
- It runs twice
- It does not run — only deferred statements that were reached are scheduled
Answer: It does not run — only deferred statements that were reached are scheduled. A defer only schedules its block once execution reaches the defer statement.
Which keyword would NOT be valid in a guard's else block inside a regular (non-throwing, non-loop) function?
- return
- continue
- fatalError()
- throw is not allowed in a non-throwing function
Answer: continue. continue and break are only valid inside loops; in a plain function you use return, throw (if throwing), or fatalError().
A common use of defer is to...
- Allocate memory
- Speed up loops
- Guarantee cleanup like closing a file or resource runs no matter how the scope exits
- Define protocols
Answer: Guarantee cleanup like closing a file or resource runs no matter how the scope exits. defer is ideal for cleanup — closing files, releasing locks — that must happen on every exit path.