JSTAcademy
0 XP
Dashboard
Crash Courses
Control Flow, Functions & Closures
16 min
Masters+170 XP
Crash Courses · Masters

Control Flow, Functions & Closures

Write expressive Swift control flow with if/guard/switch/for, design functions with argument labels and default parameters, and master closures.
16 min read+170 XP on completionCert: Swift Crash Course
Tap any word in the text below to start reading from there.

Control Flow, Functions & Closures

Control Flow: if, guard, switch

// if/else — no parentheses required around condition
let score = 87
let grade: String
if score >= 90      { grade = "A" }
else if score >= 80 { grade = "B" }
else if score >= 70 { grade = "C" }
else                { grade = "F" }

// Ternary
let status = score >= 60 ? "Pass" : "Fail"

// guard — early exit (same as guard let but for conditions)
func processScore(_ score: Int) -> String {
    guard score >= 0 && score <= 100 else {
        return "Invalid score"
    }
    return "Valid: \(score)"
}

Swift switch — exhaustive and powerful:

// Must handle all cases (or use default:)
let day = "Wednesday"
switch day {
case "Saturday", "Sunday":
    print("Weekend")
case "Monday", "Tuesday", "Wednesday", "Thursday", "Friday":
    print("Weekday")
default:
    print("Unknown")
}

// Range matching
let temp = 23
switch temp {
case ..<0:     print("Freezing")
case 0..<15:   print("Cold")
case 15..<25:  print("Comfortable")
case 25...:    print("Hot")
default:       break
}

// Tuple pattern matching
let point = (2, 0)
switch point {
case (0, 0):      print("Origin")
case (let x, 0):  print("On x-axis at \(x)")
case (0, let y):  print("On y-axis at \(y)")
case (let x, let y): print("Point at (\(x), \(y))")
}

---

Functions: Labels and Parameters

Swift functions have argument labels (external) and parameter names (internal):

// External label "to", internal name "recipient"
func sendEmail(to recipient: String, subject: String, body: String) {
    print("To: \(recipient)")
    print("Subject: \(subject)")
    print("\(body)")
}
// Called with labels:
sendEmail(to: "alice@example.com", subject: "Hello", body: "How are you?")

// No external label with _
func multiply(_ a: Int, by b: Int) -> Int {
    return a * b
}
let result = multiply(5, by: 3)  // 15

// Default parameter values
func greet(_ name: String, prefix: String = "Hello") -> String {
    return "\(prefix), \(name)!"
}
print(greet("Jordan"))             // Hello, Jordan!
print(greet("Jordan", prefix: "Hey")) // Hey, Jordan!

// Variadic parameters
func sum(_ numbers: Int...) -> Int {
    return numbers.reduce(0, +)
}
print(sum(1, 2, 3, 4, 5))  // 15

// inout — modify a parameter in place
func doubleInPlace(_ n: inout Int) {
    n *= 2
}
var x = 5
doubleInPlace(&x)
print(x)  // 10

---

Closures

A closure is a function without a name:

// Full closure syntax
let double: (Int) -> Int = { (n: Int) -> Int in
    return n * 2
}

// Shorthand argument names ($0 = first arg, etc.)
let double2: (Int) -> Int = { $0 * 2 }

// Trailing closure syntax when last argument
let numbers = [5, 3, 8, 1, 9, 2, 7]

let sorted = numbers.sorted(by: { a, b in a < b })
let sortedShort = numbers.sorted { $0 < $1 }   // trailing closure

let doubled = numbers.map { $0 * 2 }           // [10, 6, 16, 2, 18, 4, 14]
let evens   = numbers.filter { $0 % 2 == 0 }   // [8, 2]
let total   = numbers.reduce(0) { $0 + $1 }    // 35
let totalShort = numbers.reduce(0, +)          // 35 (operator as closure)

print(doubled)   // [10, 6, 16, 2, 18, 4, 14]
print(evens)     // [8, 2]
print(total)     // 35

Capturing values:

func makeCounter(start: Int = 0) -> () -> Int {
    var count = start
    return {
        count += 1
        return count
    }
}

let counter = makeCounter()
print(counter())  // 1
print(counter())  // 2
print(counter())  // 3

---

@escaping Closures

When a closure is stored beyond the function's lifetime, mark it @escaping:

class DataLoader {
    var completionHandlers: [() -> Void] = []

    // @escaping: closure stored in array, outlives loadData()
    func loadData(completion: @escaping () -> Void) {
        completionHandlers.append(completion)
        // In a real app: URLSession.shared.dataTask(...)
        // Later, the completion is called when data arrives
    }

    func simulateDataLoaded() {
        completionHandlers.forEach { $0() }
        completionHandlers.removeAll()
    }
}

let loader = DataLoader()
loader.loadData {
    print("Data loaded!")
}
loader.simulateDataLoaded()  // "Data loaded!"

---

Loops

// for-in with range
for i in 1...5 {
    print(i)  // 1 2 3 4 5
}

for i in 1..<5 {  // half-open range (1, 2, 3, 4)
    print(i)
}

// for-in with array
let fruits = ["apple", "banana", "cherry"]
for fruit in fruits {
    print(fruit)
}

// enumerated — get index + value
for (index, fruit) in fruits.enumerated() {
    print("\(index): \(fruit)")
}

// while
var n = 1
while n <= 100 {
    n *= 2
}
print(n)  // 128 — first power of 2 > 100

// repeat-while (do-while equivalent)
var attempts = 0
repeat {
    attempts += 1
} while attempts < 3
print(attempts)  // 3
0%