Control Flow
By the end of this lesson you'll make decisions with if / else , wield Swift's exhaustive switch with ranges and pattern matching, repeat work with for-in , while and repeat-while , and safely unwrap optionals with guard let and if let — the logic skeleton of every real Swift program.
Learn Control Flow 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️⃣ Conditionals: if / else
A conditional runs code only when a test is true. The test must be a Bool — a true/false value — which you build with comparisons like > , >= , == (equal) and != (not equal), and combine with && , || , and ! . Read this worked example and run it, then you'll write your own.
Your turn. The program below is almost finished — fill in the blanks marked ___ using the // 👉 hints, then run it and check the expected output.
2️⃣ Swift's Powerful switch
Swift's switch is in a different league from C or Java. Two rules shape everything: it must be exhaustive (cover every possible value — usually via a default: case), and there is no implicit fall-through (only the matching case runs, so you never write break ). On top of that you can match ranges ( 90...100 ), match and destructure tuples , bind matched values with let , and add extra conditions with where .
Now you finish a grade classifier. Fill in the missing range and the catch-all keyword:
3️⃣ Loops: for-in, while & repeat-while
Loops repeat work. for-in walks through a sequence — a range like 1...5 or an array. while repeats while a condition holds, checking before each pass (so it can run zero times). repeat-while checks after , so its body always runs at least once. A range with ... includes its end; with ..< it stops just before it.
4️⃣ Optionals: guard let & if let
An optional is a value that might be missing — its type ends in ? , and "missing" is written nil . You can't use an optional directly; you must unwrap it. if let (optional binding) runs a block only when a value is present. guard let is the early-exit twin: if the value is missing it forces you to leave the scope right away, and otherwise the unwrapped value stays available for the rest of the function — keeping your main logic flat instead of buried in nested if s.
Common Errors (and the fix)
- "Switch must be exhaustive" — you didn't cover every value. Add a default: case, or (for an enum) list every case so the compiler knows nothing is left out.
- Forgetting default on an Int/String switch — these types have unlimited values, so they always need a default: even if you think you covered everything.
- Using = instead of == — if x = 5 is an assignment and won't compile as a condition. Comparing values needs the double-equals == .
- Force-unwrapping with ! instead of guard let — let n = Int(text)! crashes at runtime ("Unexpectedly found nil") the moment the text isn't a number. Unwrap safely with guard let or if let .
- "'guard' body must not fall through" — every guard ... else block must exit the scope. End it with return , break , continue , or throw .
Pro Tips
- 💡 Prefer switch over long if/else if chains when you're testing one value against many cases — it's clearer and the compiler checks you covered everything.
- 💡 Reach for guard let at the top of a function to validate inputs and bail early. It keeps the "happy path" flat and unindented.
- 💡 Use where in a for loop to filter as you go: for i in 1...100 where i % 2 == 0 iterates only the even numbers.
- 💡 Half-open ranges match array indices : an array of length n has valid indices 0..<n , so you never run off the end.
📋 Quick Reference — Control Flow
Construct
Swift Syntax
if / else
if x > 5 { ... } else { ... }
ternary
let y = cond ? a : b
switch
switch v { case 1: ...; default: ... }
switch range
case 80.. < 90: ...
for-in range
for i in 1...10 { ... }
for-in array
for item in array { ... }
while
while x > 0 { ... }
repeat-while
repeat { ... } while x > 0
if let
if let n = Int(s) { ... }
guard let
guard let n = x else { return }
Frequently Asked Questions
Mini-Challenge: FizzBuzz
No blanks this time — just a brief and an outline. Write it yourself, run it, and check your output against the expected lines in the comments. FizzBuzz combines a loop, the remainder operator, and an if/else chain — exactly this lesson's toolkit.
🎉 Lesson Complete!
- ✅ if / else if / else and the ternary ?: make decisions
- ✅ switch is exhaustive with no fall-through, and matches ranges, tuples, value binding & where
- ✅ for-in iterates ranges and arrays; ... includes the end, ..< stops before it
- ✅ while tests before, repeat-while tests after (runs at least once)
- ✅ if let and guard let unwrap optionals safely — never force-unwrap user input with !
- ✅ Next lesson: Functions & Closures — package logic into reusable, named blocks
Practice quiz
Does Swift require parentheses around an if condition?
- Yes, always
- Only with &&
- No, but braces are always required
- Only for Bool
Answer: No, but braces are always required. Swift needs no parentheses around the test, but the { } braces are mandatory.
A Swift switch over an Int must be...
- Sorted
- In one line
- Wrapped in a loop
- Exhaustive (cover every value)
Answer: Exhaustive (cover every value). Switch must handle every possible value, usually via a default: case.
Does Swift fall through to the next case automatically?
- No, only the matching case runs
- Yes, like C
- Only for strings
- Only with break
Answer: No, only the matching case runs. There is no implicit fallthrough; only the matched case runs.
What does the range 1..<5 include?
- 1, 2, 3, 4, 5
- 1, 2, 3, 4
- 2, 3, 4
- Nothing
Answer: 1, 2, 3, 4. ..< is half-open: it stops before 5, giving 1, 2, 3, 4.
Which loop always runs its body at least once?
- for-in
- while
- repeat-while
- guard
Answer: repeat-while. repeat-while checks the condition after the body, so it runs at least once.
What must every guard ... else block do?
- Print an error
- Return a Bool
- Loop again
- Exit the scope (return/break/throw)
Answer: Exit the scope (return/break/throw). A guard's else must leave the current scope.
Which operator tests equality in a condition?
- ==
- =
- ===
- :=
Answer: ==. == compares values; a single = is assignment.
What does the ternary cond ? a : b do?
- Loops
- Picks a if cond is true, else b
- Throws an error
- Declares a constant
Answer: Picks a if cond is true, else b. The ternary yields a when cond is true and b otherwise.
Which combines two conditions with logical AND?
- and
- +
- &&
- &
Answer: &&. && is logical AND in Swift.
What does if let number = Int(input) do?
- Forces the value
- Throws if nil
- Declares a global
- Runs the block only if the conversion succeeds
Answer: Runs the block only if the conversion succeeds. Optional binding runs the block only when a value is present.
Continue this course
- Previous: Variables and Data Types
- Next: Functions and Closures