Skip to content
Lesson 6 of 15

Step 1 of 5 · Reading · ~3 min

Learn

Control Flow

if and switch

Swift's branching looks familiar if you have written C, Java or JavaScript — with one rule that trips up every newcomer and one construct that is far more capable than its namesake elsewhere.

A condition must be a Bool. Nothing else.

There is no truthiness in Swift. Not zero, not empty strings, not nil.

Does not compile

let count = 5
if count { print("some") }
// error: cannot convert value of type 'Int' to expected condition type 'Bool'

Compiles

let count = 5
if count > 0 { print("some") }

//> some

The same applies to strings (if !s.isEmpty) and optionals (if x != nil, or better, if let). The braces are also mandatory — Swift has no brace-less single-statement if.

if / else if / else

let score = 87

if score >= 90 {
    print("A")
} else if score >= 80 {
    print("B")
} else {
    print("F")
}

//> B

Order matters: the first matching branch wins and the rest are skipped. 87 satisfies score >= 80, and because score >= 90 was tested first and failed, B is correct rather than accidental.

For a two-way choice that produces a value, the ternary is shorter:

let n = 4
print(n % 2 == 0 ? "even" : "odd")

//> even

switch is a pattern matcher

Swift's switch is not C's. Three differences matter immediately:

C / Java / JavaScriptSwift
Falls through to the next caseyes, unless you breakno, never by default
Must cover every valuenoyes — or supply default
What a case can matchconstantsranges, tuples, types, conditions
let n = 7

switch n {
case 0:
    print("zero")
case 1...10:
    print("low")
case let x where x < 0:
    print("negative: \(x)")
default:
    print("large")
}

//> low

Three things are happening there. case 1...10 matches a range. case let x where x < 0 binds the value to x and then applies an extra condition. default catches everything else, and without it the compiler would reject the switch as non-exhaustive.

You can also match tuples and ignore parts with _:

let point = (2, 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, y):
    print("at \(x), \(y)")
}

//> on the x axis

Cases are tested top to bottom and the first match wins — (2, 0) also matches the final case let (x, y), but never reaches it. That ordering rule is the single most important thing to remember about switch, and it is exactly what your exercise depends on.

Comma-separated cases, and opting in to fall-through

let day = "sat"

switch day {
case "sat", "sun":
    print("weekend")
default:
    print("weekday")
}

//> weekend

One case, two patterns. If you genuinely want C-style fall-through into the next case, the keyword fallthrough opts in — it is rare, and the fact that you have to ask for it is the point.

Your exercise

Read one integer and print FizzBuzz, Fizz, Buzz, or the number.

The mistake the grader catches is testing the wrong divisor first. Every multiple of 15 is also a multiple of 3 and of 5, so if your first branch is n % 3 == 0 then input 15 prints Fizz and the very first visible test fails:

Input% 3 checked first% 15 checked first
15FizzFizzBuzz
9FizzFizz
10BuzzBuzz

Put the 15 case first, whether you use if / else if or a switch with a where clause. The hidden test feeds 7 and wants the bare number, so your final branch must print n on its own with no surrounding text.

Up nextLoopsControl Flow

Discussion

Ask a question, share an insight, or help someone who’s stuck.

Sign in to post a comment or reply.

Loading…