5. Booleans & conditionals¶
What & why¶
Programs make decisions. Swift's Bool type, if, switch, and guard give you precise, hard-to-misuse ways to branch. In particular, switch is far more powerful than in C-family languages — it's one of Swift's signature features.
Bool and comparison¶
let age = 20
print(age >= 18) // true
print(age == 20) // true
print(age != 20) // false
print("a" < "b") // true
Comparison operators: == != < <= > >=. Conditions must be a real Bool — Swift has no "truthy" values:
let count = 3
// if count { } // ERROR: 'Int' is not convertible to 'Bool'
if count != 0 { print("non-zero") }
Logical operators¶
let hasTicket = true
let isVIP = false
print(hasTicket && isVIP) // false (and)
print(hasTicket || isVIP) // true (or)
print(!hasTicket) // false (not)
&& and || short-circuit: the right side runs only if needed.
let items: [Int] = []
if !items.isEmpty && items[0] > 5 { } // safe: items[0] is never evaluated when empty
Toggle a stored Bool with flag.toggle().
if / else if / else¶
No parentheses around the condition; braces are mandatory:
let temperature = 28
if temperature > 30 {
print("hot")
} else if temperature > 20 {
print("warm")
} else {
print("cool")
}
if as an expression¶
Since Swift 5.9, if (and switch) can produce a value:
Every branch must provide a value of the same type, and else is required.
Ternary operator¶
Fine for simple choices; use if expressions when it gets long. Don't nest ternaries.
switch¶
let day = 3
switch day {
case 1:
print("Monday")
case 2:
print("Tuesday")
case 3:
print("Wednesday")
default:
print("Another day")
}
Differences from C that matter:
- No fallthrough. Each case ends automatically; no
breakneeded. - Exhaustive. Every possible value must be covered — usually with
default. (With enums, the compiler checks that you listed every case, nodefaultneeded.) - Every case must contain at least one statement.
Multiple values, ranges, tuples, where¶
switch day {
case 1, 2, 3, 4, 5:
print("Weekday")
case 6, 7:
print("Weekend")
default:
print("Invalid")
}
let score = 87
switch score {
case 90...100: print("A")
case 80..<90: print("B")
case 70..<80: print("C")
case ..<70: print("Below C")
default: print("Out of range")
}
let point = (x: 2, y: 0)
switch point {
case (0, 0): print("origin")
case (_, 0): print("on the x-axis")
case (0, _): print("on the y-axis")
case (let x, let y) where x == y: print("on the diagonal")
default: print("somewhere else")
}
Value binding¶
switch point {
case (let x, 0): print("x-axis at \(x)")
case (0, let y): print("y-axis at \(y)")
case let (x, y): print("(\(x), \(y))")
}
Switching on strings and switch as an expression¶
Need C-style fall-through? Write the fallthrough keyword explicitly — rarely a good idea.
guard — early exit¶
guard checks a condition that must be true to continue, and requires you to leave the scope if it isn't:
func describe(age: Int) {
guard age >= 0 else {
print("Invalid age")
return
}
print("Age is \(age)")
}
It keeps the "happy path" un-nested. You'll use it constantly with optionals (lesson 10).
Comparing with ranges¶
let r = 1...10 // closed range: 1 through 10
print(r.contains(5)) // true
print(5 ~= r) // true (the pattern-match operator, used implicitly in switch)
Common mistakes¶
- Using
=instead of==in a condition: Swift rejects it (=returnsVoid), which is a safety win. - Forgetting
defaultin aswitchover anIntorString. - Expecting fallthrough from one case to the next.
- Writing parentheses and semicolons out of habit — legal but unidiomatic.
- Deep nesting instead of
guardearly-exits. - Comparing floating-point values with
==(see lesson 3).
Exercises¶
- Write code that prints whether a year is a leap year (divisible by 4, except centuries not divisible by 400).
- Given a
scorefrom 0–100, print a letter grade usingswitchwith ranges. - Use
switchon a tuple(Int, Int)to classify a point as origin / x-axis / y-axis / quadrant 1–4. - Write
func sign(_ n: Int) -> Stringreturning"negative","zero"or"positive"using anifexpression. - Rewrite a nested
ifpyramid (3 levels deep) usingguardearly exits. - Read a command (
"add","remove","list", anything else) withreadLine()and respond usingswitch.
Recap¶
- Conditions must be
Bool; no truthy/falsy values. &&/||short-circuit.ifandswitchcan be expressions that return a value.switchis exhaustive, has no fallthrough, and matches ranges, tuples, bindings andwhereclauses.guard ... else { return }keeps code flat and safe.
Next: Loops →