Control Flow in Swift: `if`, `switch`, `guard`, and Pattern Matching
Every program needs to make decisions. Should Buzz Lightyear fly or fall? Does Remy get to cook, or does he hide? Without control flow, your code would run every line from top to bottom with no ability to choose a different path based on conditions.
In this guide, you’ll learn Swift’s three main decision-making tools: if/else, switch, and guard. We won’t cover
loops or optionals here — those each have their own dedicated posts.
This guide assumes you’re familiar with operators and data types.
What You’ll Learn
- What Is Control Flow?
if,else if, andelseswitchStatements- Pattern Matching with
switch guardfor Early Exits- Common Mistakes
- What’s Next?
What Is Control Flow?
Control flow is how your program decides which code to run based on conditions. Think of it like the sorting station at Monsters, Inc.: each door leads to a different room, and the system decides which door to open based on the current task.
Without control flow, every line of code runs in order, every time. With control flow, your program can take different paths depending on the data it’s working with.
if, else if, and else
The if statement is the simplest way to make a decision. If a condition is true, the code inside the braces runs.
let scarePower = 95
if scarePower > 90 {
print("Top scarer!")
}
Top scarer!
Adding Alternatives
Use else to provide a fallback, and else if to check additional conditions:
let scarePower = 75
if scarePower > 90 {
print("Top scarer!")
} else if scarePower > 50 {
print("Solid performer")
} else {
print("Needs training")
}
Solid performer
Swift evaluates conditions from top to bottom and runs the first block that matches. Once a match is found, it skips all remaining branches.
Combining Conditions
You can combine conditions using && (and) and || (or):
let age = 8
let hasTicket = true
if age >= 5 && hasTicket {
print("Enjoy the Pixar movie marathon!")
}
Enjoy the Pixar movie marathon!
Tip: Keep conditions readable. If you have more than two or three combined conditions, consider extracting them into a descriptive variable:
let canEnter = age >= 5 && hasTicket
switch Statements
When you’re checking one value against many possible cases, switch is cleaner than a chain of if/else if statements.
let movie = "Coco"
switch movie {
case "Toy Story":
print("A story about toys coming to life")
case "Finding Nemo":
print("A clownfish searches for his son")
case "Coco":
print("A boy journeys to the Land of the Dead")
default:
print("Another great Pixar film")
}
A boy journeys to the Land of the Dead
Exhaustive Matching
Swift requires every switch to be exhaustive — it must handle every possible value. For strings and numbers, this
means you need a default case as a catch-all.
Note: This is a safety feature. Swift won’t let you accidentally forget a case. The compiler will tell you if your
switchisn’t exhaustive.
Multiple Values in One Case
You can match several values in a single case using commas:
let rating = "PG"
switch rating {
case "G", "PG":
print("Family friendly")
case "PG-13":
print("Some material may be inappropriate")
case "R":
print("Restricted")
default:
print("Unknown rating")
}
Family friendly
Ranges in Switch
switch works beautifully with ranges:
let audienceScore = 87
switch audienceScore {
case 90...100:
print("Certified Fresh — masterpiece!")
case 70..<90:
print("Fresh — worth watching")
case 50..<70:
print("Mixed reviews")
default:
print("Rotten — skip it")
}
Fresh — worth watching
Note: Swift’s
switchdoes not fall through to the next case by default. Unlike C or Java, you don’t need to writebreakat the end of each case. Each case automatically stops after its code runs.
Pattern Matching with switch
Swift’s switch goes far beyond simple equality checks. You can match tuples and use the where clause to add
extra conditions.
Matching Tuples
let coordinates = (0, 10)
switch coordinates {
case (0, 0):
print("At the origin")
case (_, 0):
print("On the x-axis")
case (0, _):
print("On the y-axis")
default:
print("Somewhere else: \(coordinates)")
}
On the y-axis
The underscore _ acts as a wildcard — it matches any value. So (0, _) means “x is 0 and y can be anything.”
The where Clause
Add a where clause to filter cases with extra conditions:
let character = (name: "Buzz", age: 5)
switch character {
case let (name, age) where age < 10:
print("\(name) is a young toy")
case let (name, _):
print("\(name) is experienced")
}
Buzz is a young toy
The where clause lets you bind values and test them in one step. This becomes especially powerful when combined with
enums and optionals.
Apple Docs:
Control Flow— The Swift Programming Language
guard for Early Exits
The guard statement is like a bouncer at the door of Gusteau’s restaurant in Ratatouille. It checks a condition at
the start and turns away anything that doesn’t qualify, so the rest of your code can proceed without worrying.
func printMovieReview(title: String, score: Int) {
guard score >= 0 && score <= 100 else {
print("Invalid score for \(title)")
return
}
print("\(title) scored \(score)/100")
}
printMovieReview(title: "Inside Out", score: 95)
printMovieReview(title: "Bad Movie", score: -5)
Inside Out scored 95/100
Invalid score for Bad Movie
How guard Differs from if
The key difference: the else block in a guard statement must exit the current scope — using return, break,
continue, or throw. This guarantees that after a guard passes, the condition is definitely true for all code that
follows.
func greetCharacter(name: String?) {
guard let characterName = name else {
print("No character name provided")
return
}
// characterName is guaranteed to exist here
print("Hello, \(characterName)!")
}
greetCharacter(name: "Woody")
greetCharacter(name: nil)
Hello, Woody!
No character name provided
Tip: Use
guardwhen you want to validate inputs early and avoid deeply nestedifstatements. It keeps your main logic at the top level of indentation.
Common Mistakes
Forgetting That switch Doesn’t Fall Through
// ❌ Expecting fall-through like C/Java
let genre = "Animation"
switch genre {
case "Animation":
print("Animated film")
// Does NOT fall through to the next case
case "Comedy":
print("Funny film")
default:
print("Other genre")
}
Animated film
// ✅ Use fallthrough explicitly if you need it
let genre = "Animation"
switch genre {
case "Animation":
print("Animated film")
fallthrough
case "Comedy":
print("Funny film")
default:
print("Other genre")
}
Animated film
Funny film
In Swift, switch cases don’t fall through by default. If you actually need fall-through behavior, use the
fallthrough keyword — but this is rare in practice.
Non-Exhaustive switch Statements
// ❌ Won't compile — missing cases
let day = "Monday"
// switch day {
// case "Monday":
// print("Start of the week")
// case "Friday":
// print("Almost weekend")
// } // Error: switch must be exhaustive
// ✅ Add a default case
let day = "Monday"
switch day {
case "Monday":
print("Start of the week")
case "Friday":
print("Almost weekend")
default:
print("Regular day")
}
Start of the week
Every switch must cover all possible values. For open-ended types like String and Int, always include a default
case.
What’s Next?
if/elsehandles simple branching decisionsswitchmatches values against multiple cases and must be exhaustive- Swift’s
switchdoes not fall through by default — nobreakneeded guardvalidates conditions early and forces an exit if they fail- Pattern matching with tuples and
wheremakesswitchvery powerful
Now that your code can make decisions, it needs to repeat actions. Head over to
Loops in Swift to learn about for-in, while, and repeat-while.