Lesson 82Error Handling

Throwing Functions

The throws Keyword

Functions that can fail are marked with throws in their signature. This tells the compiler and other developers that this function might throw an error.

Syntax

func doSomething() throws -> ReturnType

Where throws Can Be Used

Functions

Regular throwing functions

Initializers

Failable init with throw

Closures

Throwing closure types

Subscripts

Swift 5.5+ feature

throws vs rethrows

throws

Function always requires try when called

rethrows

Only throws if the closure parameter throws

Common Pattern

Use guard statements with throw for validation:

guard condition else { throw MyError.invalidInput }

Async + Throws

For async functions that can fail:

func fetch() async throws -> Data

Note: async comes before throws

main.swift
// THROWING FUNCTIONS
// Functions that can fail are marked with 'throws'

// BASIC THROWING FUNCTION
enum DivisionError: Error {
    case divisionByZero
}

func divide(_ a: Int, by b: Int) throws -> Int {
    if b == 0 {
        throw DivisionError.divisionByZero
    }
    return a / b
}

// THROWING WITH MULTIPLE ERROR CASES
enum WithdrawalError: Error {
    case insufficientFunds(available: Double)
    case accountLocked
    case dailyLimitExceeded(limit: Double)
}

class BankAccount {
    var balance: Double = 1000
    var isLocked: Bool = false
    let dailyLimit: Double = 500

    func withdraw(amount: Double) throws -> Double {
        guard !isLocked else {
            throw WithdrawalError.accountLocked
        }

        guard amount <= dailyLimit else {
            throw WithdrawalError.dailyLimitExceeded(limit: dailyLimit)
        }

        guard amount <= balance else {
            throw WithdrawalError.insufficientFunds(available: balance)
        }

        balance -= amount
        return amount
    }
}

// THROWING INITIALIZERS
struct User {
    let email: String
    let password: String

    enum ValidationError: Error {
        case invalidEmail
        case passwordTooShort
    }

    init(email: String, password: String) throws {
        guard email.contains("@") else {
            throw ValidationError.invalidEmail
        }
        guard password.count >= 8 else {
            throw ValidationError.passwordTooShort
        }

        self.email = email
        self.password = password
    }
}

// THROWING CLOSURES
let riskyOperation: () throws -> Int = {
    let random = Int.random(in: 1...10)
    if random < 5 {
        throw NSError(domain: "RandomError", code: random)
    }
    return random
}

// FUNCTION THAT ACCEPTS THROWING CLOSURE
func performOperation(_ operation: () throws -> Int) throws -> Int {
    print("Starting operation...")
    let result = try operation()
    print("Operation completed!")
    return result
}

// RETHROWS - Function throws only if its closure throws
func transform<T>(_ items: [T], using closure: (T) throws -> T) rethrows -> [T] {
    var result: [T] = []
    for item in items {
        result.append(try closure(item))
    }
    return result
}

// Non-throwing closure - no try needed
let doubled = transform([1, 2, 3]) { $0 * 2 }
print(doubled)  // [2, 4, 6]

// Throwing closure - try needed
enum TransformError: Error { case negative }
let numbers = try transform([1, 2, -3]) { n in
    if n < 0 { throw TransformError.negative }
    return n * 2
}

// THROWING COMPUTED PROPERTIES (Swift 5.5+)
struct Config {
    private var data: [String: Any] = [:]

    enum ConfigError: Error {
        case missingKey(String)
    }

    subscript(key: String) -> Any {
        get throws {
            guard let value = data[key] else {
                throw ConfigError.missingKey(key)
            }
            return value
        }
    }
}

// ASYNC THROWING FUNCTIONS
func fetchUser(id: Int) async throws -> String {
    // Simulating network call
    if id < 0 {
        throw NetworkError.invalidID
    }
    return "User \(id)"
}

enum NetworkError: Error {
    case invalidID
}

Try It Yourself!

Create a FileManager class with a throwing readFile(name:) method that throws different errors for missing files, permission denied, and corrupted files!