Swift Error Handling

Managing and responding to error conditions in your code

โš ๏ธ What is Error Handling?

Error handling allows you to respond to and recover from error conditions in your program. Swift provides first-class support for throwing, catching, propagating, and manipulating recoverable errors at runtime.


enum ValidationError: Error {
    case tooShort
    case tooLong
    case invalidCharacters
}

func validatePassword(_ password: String) throws -> Bool {
    if password.count < 6 {
        throw ValidationError.tooShort
    }
    if password.count > 20 {
        throw ValidationError.tooLong
    }
    return true
}

do {
    try validatePassword("abc")
} catch ValidationError.tooShort {
    print("Password is too short!")
} catch {
    print("Other error: \(error)")
}
                                    

Output:

Password is too short!

Error Handling Components

๐Ÿšจ

Throw

Functions can throw errors using throw keyword

func riskyFunction() throws {
    throw MyError.failed
}
๐ŸŽฏ

Try

Call throwing functions with try keyword

do {
    try riskyFunction()
}
๐Ÿ›ก๏ธ

Catch

Handle specific errors with catch blocks

catch MyError.failed {
    print("Function failed")
}
๐Ÿ“‹

Error Protocol

Define custom errors conforming to Error

enum MyError: Error {
    case failed
}

๐Ÿ”น Basic Error Handling

Here's how to define and handle errors:

// Define custom errors
enum MathError: Error {
    case divisionByZero
    case negativeNumber
}

// Function that can throw errors
func divide(_ a: Double, by b: Double) throws -> Double {
    if b == 0 {
        throw MathError.divisionByZero
    }
    return a / b
}

func squareRoot(_ number: Double) throws -> Double {
    if number < 0 {
        throw MathError.negativeNumber
    }
    return number.squareRoot()
}

// Using do-catch to handle errors
do {
    let result1 = try divide(10, by: 2)
    print("10 รท 2 = \(result1)")
    
    let result2 = try divide(10, by: 0) // This will throw
    print("This won't print")
} catch MathError.divisionByZero {
    print("Error: Cannot divide by zero!")
} catch MathError.negativeNumber {
    print("Error: Cannot calculate square root of negative number!")
}

Output:

10 รท 2 = 5.0

Error: Cannot divide by zero!

๐Ÿ”น Different Ways to Handle Errors

Swift provides several ways to handle throwing functions:

// Method 1: try? - Convert to optional
let result1 = try? divide(10, by: 2)  // Optional(5.0)
let result2 = try? divide(10, by: 0)  // nil

if let safeResult = result1 {
    print("Safe result: \(safeResult)")
}

// Method 2: try! - Force unwrap (use carefully)
let result3 = try! divide(10, by: 2)  // 5.0 (crashes if error)
print("Forced result: \(result3)")

// Method 3: Nil coalescing with try?
let result4 = (try? divide(10, by: 0)) ?? 0
print("Result with default: \(result4)")

// Method 4: Multiple catch blocks
do {
    let value = try squareRoot(-4)
    print("Square root: \(value)")
} catch MathError.negativeNumber {
    print("Cannot calculate square root of negative number")
} catch {
    print("Unexpected error: \(error)")
}

Output:

Safe result: 5.0

Forced result: 5.0

Result with default: 0.0

Cannot calculate square root of negative number

๐Ÿง  Test Your Knowledge

Which keyword is used to call a function that can throw errors?