Swift Control Flow - Making Decisions in Code
Welcome to Swift Control Flow! Control flow is how you direct the execution of your code based on conditions. Think of it as teaching your program to make decisions, just like you do in real life. In this guide, we’ll explore how to make your Swift code smart and responsive using various control flow statements.
What is Control Flow?
Control flow determines the order in which your code executes. Instead of running every line from top to bottom, control flow allows your program to:
- Make decisions based on conditions
- Choose different paths depending on values
- Skip or execute code blocks selectively
- Handle different scenarios elegantly
Swift provides several powerful control flow tools that make your code both readable and efficient.
If-Else Statements
The if statement is the most fundamental control flow tool. It lets you execute code only when a certain condition is true.
Basic If Statement
let temperature = 25
if temperature > 20 { print("It's a warm day!")}// Output: It's a warm day!If-Else Statement
When you want to handle both true and false cases:
let age = 16
if age >= 18 { print("You can vote!")} else { print("You're too young to vote.")}// Output: You're too young to vote.If-Else If-Else Chain
Handle multiple conditions in sequence:
let score = 85
if score >= 90 { print("Grade: A - Excellent!")} else if score >= 80 { print("Grade: B - Great job!")} else if score >= 70 { print("Grade: C - Good work!")} else if score >= 60 { print("Grade: D - Needs improvement")} else { print("Grade: F - Study harder!")}// Output: Grade: B - Great job!Combining Conditions
Use logical operators to combine multiple conditions:
let username = "admin"let password = "secret123"
// AND operator (&&) - both conditions must be trueif username == "admin" && password == "secret123" { print("Login successful!")} else { print("Invalid credentials")}// Output: Login successful!
// OR operator (||) - at least one condition must be truelet dayOfWeek = "Saturday"
if dayOfWeek == "Saturday" || dayOfWeek == "Sunday" { print("It's the weekend! 🎉")} else { print("It's a weekday")}// Output: It's the weekend! 🎉
// NOT operator (!) - inverts the conditionlet isRaining = false
if !isRaining { print("Great weather for a walk!")}// Output: Great weather for a walk!Nested If Statements
You can nest if statements inside each other:
let hasTicket = truelet hasID = truelet age = 25
if hasTicket { if hasID { if age >= 18 { print("Welcome to the concert!") } else { print("Sorry, adults only") } } else { print("ID required") }} else { print("Please purchase a ticket")}// Output: Welcome to the concert!Better approach using combined conditions:
if hasTicket && hasID && age >= 18 { print("Welcome to the concert!")} else if !hasTicket { print("Please purchase a ticket")} else if !hasID { print("ID required")} else { print("Sorry, adults only")}Ternary Conditional Operator
The ternary operator is a shorthand for simple if-else statements. It’s called “ternary” because it takes three operands.
Syntax
condition ? valueIfTrue : valueIfFalseBasic Examples
let age = 20let canVote = age >= 18 ? "Yes" : "No"print("Can vote: \(canVote)")// Output: Can vote: Yes
let temperature = 15let weatherDescription = temperature > 20 ? "Warm" : "Cold"print("Weather: \(weatherDescription)")// Output: Weather: Cold
// Assign different values based on conditionlet score = 85let result = score >= 60 ? "Pass" : "Fail"print(result) // Output: PassPractical Use Cases
// Setting display textlet itemCount = 1let message = itemCount == 1 ? "1 item" : "\(itemCount) items"print(message) // Output: 1 item
// Pricing logiclet isMember = truelet price = isMember ? 9.99 : 14.99print("Price: $\(price)") // Output: Price: $9.99
// Status indicatorslet isOnline = truelet status = isOnline ? "🟢 Online" : "🔴 Offline"print(status) // Output: 🟢 OnlineWhen to Use Ternary vs If-Else
Use Ternary When:
- ✅ Assigning one of two values to a variable
- ✅ The logic is simple and fits on one line
- ✅ It makes the code more concise and readable
Use If-Else When:
- ✅ You need to execute multiple statements
- ✅ The logic is complex or nested
- ✅ You need more than two outcomes
// ✅ Good use of ternary - simple and clearlet badge = points > 100 ? "Gold" : "Silver"
// ❌ Bad use of ternary - too complexlet badge = points > 100 ? (isPremium ? "Platinum" : "Gold") : (isActive ? "Silver" : "Bronze")
// ✅ Better as if-elselet badge: Stringif points > 100 { badge = isPremium ? "Platinum" : "Gold"} else { badge = isActive ? "Silver" : "Bronze"}Switch Statements
Switch statements provide a clean way to handle multiple possible values. They’re especially powerful in Swift and can do much more than in other languages!
Basic Switch
let dayNumber = 3
switch dayNumber {case 1: print("Monday")case 2: print("Tuesday")case 3: print("Wednesday")case 4: print("Thursday")case 5: print("Friday")case 6: print("Saturday")case 7: print("Sunday")default: print("Invalid day")}// Output: WednesdayImportant: No Fallthrough
Unlike many other languages, Swift doesn’t fall through to the next case automatically:
let number = 1
switch number {case 1: print("One") // Automatically breaks here - no fallthrough!case 2: print("Two")default: print("Other")}// Output: OneMultiple Values per Case
Combine multiple values in a single case:
let character = "a"
switch character {case "a", "e", "i", "o", "u": print("\(character) is a vowel")case "b", "c", "d", "f", "g": print("\(character) is a consonant")default: print("Unknown character")}// Output: a is a vowelRange Matching
Use ranges to match a range of values:
let score = 85
switch score {case 0..<60: print("F - Keep studying!")case 60..<70: print("D - You can do better")case 70..<80: print("C - Good effort")case 80..<90: print("B - Great work!")case 90...100: print("A - Excellent!")default: print("Invalid score")}// Output: B - Great work!Tuple Matching
Switch can work with tuples to match multiple values:
let coordinate = (x: 0, y: 0)
switch coordinate {case (0, 0): print("Origin point")case (_, 0): print("On the x-axis")case (0, _): print("On the y-axis")case (-2...2, -2...2): print("Inside the 2x2 box")default: print("Outside the box")}// Output: Origin pointValue Binding
Bind matched values to temporary variables:
let point = (x: 2, y: 0)
switch point {case (0, 0): print("At origin")case (let x, 0): print("On x-axis at x = \(x)")case (0, let y): print("On y-axis at y = \(y)")case let (x, y): print("At (\(x), \(y))")}// Output: On x-axis at x = 2Where Clauses
Add additional conditions to cases:
let temperature = 25let isRaining = false
switch temperature {case let temp where temp < 0: print("Freezing! \(temp)°C")case let temp where temp < 15: print("Cold: \(temp)°C")case let temp where temp < 25 && !isRaining: print("Perfect weather: \(temp)°C")case let temp where temp < 25 && isRaining: print("Mild but rainy: \(temp)°C")case let temp where temp >= 25: print("Hot: \(temp)°C")default: print("Unknown")}// Output: Perfect weather: 25°CSwitch with Enums
Switch works beautifully with enumerations:
enum Weather { case sunny case cloudy case rainy case snowy}
let today = Weather.sunny
switch today {case .sunny: print("☀️ Wear sunglasses!")case .cloudy: print("☁️ Gray day")case .rainy: print("🌧️ Bring an umbrella!")case .snowy: print("❄️ Time for a snowman!")}// Output: ☀️ Wear sunglasses!Switch Must Be Exhaustive
Swift requires that switch statements handle all possible values:
let number = 5
// ❌ Error: Switch must be exhaustive// switch number {// case 1:// print("One")// }
// ✅ Correct - handles all casesswitch number {case 1: print("One")default: print("Not one")}Guard Statements
Guard statements are designed for early exits. They’re perfect for validating conditions at the beginning of a function.
Basic Guard
func greet(name: String?) { guard let unwrappedName = name else { print("No name provided") return }
print("Hello, \(unwrappedName)!")}
greet(name: "Alice") // Output: Hello, Alice!greet(name: nil) // Output: No name providedWhy Use Guard?
Guard statements improve code readability by handling error cases first:
// ❌ Without guard - nested and hard to readfunc processOrder(item: String?, quantity: Int?, price: Double?) { if let item = item { if let quantity = quantity { if let price = price { if quantity > 0 && price > 0 { let total = Double(quantity) * price print("Order: \(quantity) × \(item) = $\(total)") } else { print("Invalid quantity or price") } } else { print("Price missing") } } else { print("Quantity missing") } } else { print("Item missing") }}
// ✅ With guard - clean and readable!func processOrderBetter(item: String?, quantity: Int?, price: Double?) { guard let item = item else { print("Item missing") return }
guard let quantity = quantity else { print("Quantity missing") return }
guard let price = price else { print("Price missing") return }
guard quantity > 0 && price > 0 else { print("Invalid quantity or price") return }
let total = Double(quantity) * price print("Order: \(quantity) × \(item) = $\(total)")}
processOrderBetter(item: "iPhone", quantity: 2, price: 999.99)// Output: Order: 2 × iPhone = $1999.98Multiple Conditions in Guard
You can unwrap multiple optionals in one guard statement:
func createUser(username: String?, email: String?, age: Int?) { guard let username = username, let email = email, let age = age else { print("Missing required information") return }
guard age >= 13 else { print("Must be at least 13 years old") return }
print("User created: \(username) (\(email)), Age: \(age)")}
createUser(username: "alice", email: "alice@email.com", age: 25)// Output: User created: alice (alice@email.com), Age: 25
createUser(username: "bob", email: nil, age: 30)// Output: Missing required informationGuard vs If-Let
When to use each:
// Use IF-LET when you want to do something WITH the valuefunc printName(name: String?) { if let name = name { print("Name is: \(name)") // Continue working with name }}
// Use GUARD when you want to exit early WITHOUT the valuefunc validateName(name: String?) -> Bool { guard let name = name else { return false }
// name is available for rest of function return name.count >= 3}Guard in Loops
Guard works great in loops to skip invalid items:
let numbers = [1, 2, -5, 4, -10, 6]
for number in numbers { guard number > 0 else { print("Skipping negative: \(number)") continue }
print("Processing: \(number)")}
// Output:// Processing: 1// Processing: 2// Skipping negative: -5// Processing: 4// Skipping negative: -10// Processing: 6Practical Examples
Example 1: User Authentication
func authenticateUser(username: String?, password: String?) -> Bool { guard let username = username, let password = password else { print("❌ Username and password required") return false }
guard !username.isEmpty && !password.isEmpty else { print("❌ Username and password cannot be empty") return false }
guard username.count >= 3 else { print("❌ Username must be at least 3 characters") return false }
guard password.count >= 8 else { print("❌ Password must be at least 8 characters") return false }
// Simulate authentication if username == "admin" && password == "password123" { print("✅ Login successful!") return true } else { print("❌ Invalid credentials") return false }}
authenticateUser(username: "admin", password: "password123")// Output: ✅ Login successful!Example 2: Grade Calculator
func calculateGrade(score: Int) -> String { guard score >= 0 && score <= 100 else { return "Invalid score" }
switch score { case 90...100: return "A - Outstanding! 🌟" case 80..<90: return "B - Excellent work! 🎉" case 70..<80: return "C - Good job! 👍" case 60..<70: return "D - Needs improvement 📚" default: return "F - Please study harder 📖" }}
print(calculateGrade(score: 95)) // A - Outstanding! 🌟print(calculateGrade(score: 75)) // C - Good job! 👍print(calculateGrade(score: 105)) // Invalid scoreExample 3: Shipping Calculator
func calculateShipping(weight: Double?, destination: String?) -> Double? { guard let weight = weight, let destination = destination else { print("Missing weight or destination") return nil }
guard weight > 0 else { print("Invalid weight") return nil }
let baseRate: Double
switch destination.lowercased() { case "local": baseRate = 5.0 case "national": baseRate = 10.0 case "international": baseRate = 20.0 default: print("Unknown destination") return nil }
let shippingCost = baseRate + (weight * 0.5)
return shippingCost}
if let cost = calculateShipping(weight: 2.5, destination: "national") { print("Shipping cost: $\(cost)")}// Output: Shipping cost: $11.25Example 4: Temperature Converter
enum TemperatureUnit { case celsius case fahrenheit case kelvin}
func convertTemperature(value: Double, from: TemperatureUnit, to: TemperatureUnit) -> Double { // Same unit, no conversion needed guard from != to else { return value }
var celsius: Double
// Convert input to Celsius first switch from { case .celsius: celsius = value case .fahrenheit: celsius = (value - 32) * 5/9 case .kelvin: celsius = value - 273.15 }
// Convert from Celsius to target unit switch to { case .celsius: return celsius case .fahrenheit: return celsius * 9/5 + 32 case .kelvin: return celsius + 273.15 }}
let temp = convertTemperature(value: 25, from: .celsius, to: .fahrenheit)print("\(temp)°F") // Output: 77.0°FBest Practices
1. Use Guard for Early Returns
// ✅ Good - fail fast with guardfunc process(data: String?) { guard let data = data else { return } // Process data}
// ❌ Avoid - unnecessary nestingfunc processData(data: String?) { if let data = data { // Process data }}2. Prefer Switch Over Multiple If-Else
// ❌ Harder to readif value == 1 { print("One")} else if value == 2 { print("Two")} else if value == 3 { print("Three")} else { print("Other")}
// ✅ Cleaner with switchswitch value {case 1: print("One")case 2: print("Two")case 3: print("Three")default: print("Other")}3. Keep Ternary Operators Simple
// ✅ Simple and readablelet status = isActive ? "Active" : "Inactive"
// ❌ Too complexlet message = count > 10 ? (isPremium ? "Premium: Many items" : "Regular: Many items") : "Few items"
// ✅ Better as if-elselet message: Stringif count > 10 { message = isPremium ? "Premium: Many items" : "Regular: Many items"} else { message = "Few items"}4. Make Switch Statements Exhaustive
enum Status { case pending case approved case rejected}
// ✅ Handles all cases explicitlyswitch status {case .pending: print("Pending")case .approved: print("Approved")case .rejected: print("Rejected")}Common Mistakes to Avoid
1. Unnecessary Force Unwrapping
// ❌ Dangerous - can crashfunc greet(name: String?) { if name != nil { print("Hello, \(name!)!") // Force unwrap is risky! }}
// ✅ Safe unwrappingfunc greetSafe(name: String?) { guard let name = name else { return } print("Hello, \(name)!")}2. Not Using Guard Properly
// ❌ Guard without return/throw/break/continue// guard condition else {// // Must exit the scope!// }
// ✅ Proper guard usageguard condition else { return // or throw, break, continue}3. Complex Nested Conditions
// ❌ Hard to readif condition1 { if condition2 { if condition3 { // Code } }}
// ✅ Use guard or combine conditionsguard condition1, condition2, condition3 else { return }// CodeSummary
Control flow is essential for making your programs smart and responsive. Here’s what we covered:
If-Else Statements 🔀
- Make decisions based on conditions
- Chain multiple if-else blocks
- Combine conditions with logical operators
Ternary Operator ❓
- Shorthand for simple if-else
- Great for simple value assignments
- Keep it simple and readable
Switch Statements 🎛️
- Handle multiple cases elegantly
- Pattern matching with ranges and tuples
- Must be exhaustive in Swift
- No automatic fallthrough
Guard Statements 🛡️
- Early exit for invalid conditions
- Unwrap optionals safely
- Keep code flat and readable
- Perfect for validation
Next Steps
Now that you’ve mastered control flow, you’re ready for the next topic!
Tomorrow, we’ll explore:
- Topic 8: Loops
- For-in loops
- While loops
- Repeat-while loops
- Loop control (break, continue)
Practice Exercises
Try these to reinforce your learning:
- Write a function that determines if a year is a leap year using if-else
- Create a switch statement that converts a number (1-12) to a month name
- Use guard statements to validate user registration data
- Build a simple calculator using switch for operations (+, -, *, /)
- Create a grade calculator that uses both guard and switch statements
Master control flow and you control your program’s destiny! 🚀
Remember: Write code that reads like a story. Use the right control flow tool for each situation.