iwantcoding.com
🔥 Daily 👥 Rooms 🏆 Top Log in Sign up

Error Handling

Swift error handling: throws + do / try / catch + Result. Recoverable vs not, and the conventions that read clean.

Swift — error handling

EXAMPLE
// ===== Define an error type =====
enum AppError: Error {
    case notFound
    case unauthorized
    case validation(String)
    case network(URLError)
}

// ===== Throwing function =====
func loadUser(id: Int) throws -> User {
    guard id > 0 else { throw AppError.validation("bad id") }
    let row = try database.query("...")
    guard let row else { throw AppError.notFound }
    return User(from: row)
}

// ===== do / try / catch =====
do {
    let user = try loadUser(id: 42)
    print(user)
} catch AppError.notFound {
    print("not found")
} catch AppError.validation(let msg) {
    print("invalid: \(msg)")
} catch {
    print("other: \(error)")
}

// ===== try? and try! =====
let user: User? = try? loadUser(id: 42)      // converts error to nil
let user2: User = try! loadUser(id: 42)      // crash on error (use sparingly)

// ===== Result =====
let result: Result<User, AppError> = .success(User(id: 1, name: "Alex"))

switch result {
case .success(let u): print(u)
case .failure(let e): print(e)
}

// Chain via map + flatMap:
let upper = result.map { \$0.name.uppercased() }

// ===== Rethrows (passes errors from closures up) =====
func transform<T>(_ value: T, _ f: (T) throws -> T) rethrows -> T {
    return try f(value)
}

// ===== async / await + throws =====
func fetchUser(id: Int) async throws -> User {
    let (data, _) = try await URLSession.shared.data(from: url)
    return try JSONDecoder().decode(User.self, from: data)
}

// Call:
Task {
    do {
        let user = try await fetchUser(id: 42)
        print(user)
    } catch {
        print(error)
    }
}

// ===== Custom error with LocalizedError =====
enum APIError: LocalizedError {
    case network
    case decoding
    case unauthorized

    var errorDescription: String? {
        switch self {
        case .network:     return "Network error"
        case .decoding:    return "Decoding failed"
        case .unauthorized: return "Sign in again"
        }
    }
}

// ===== Re-throwing / wrapping =====
do {
    let user = try await fetchUser(id: 42)
} catch let urlError as URLError {
    throw AppError.network(urlError)    // wrap for callers
} catch {
    throw error                          // re-throw unknown
}

// ===== defer (cleanup that runs even on throw) =====
func process() throws {
    let file = try openFile("data.txt")
    defer { file.close() }
    try parse(file)
}

// ===== When to use what =====
// throws / try:    library APIs; recoverable failures with multiple cases
// Result:          callback / async results; storing the result
// Optional / nil:  simple absence; one failure mode
// fatalError():    programmer errors only; never recoverable

// ===== Patterns to internalise =====
// - Custom Error enum with cases for the failure modes
// - LocalizedError for user-facing messages
// - try? for 'don't care about the error' paths
// - defer for cleanup

// ===== Pitfalls =====
// - try! everywhere -> crashes you would not have with proper catch
// - Catch-all 'catch { }' that swallows errors silently
// - Using Result<T, Error> when a typed enum would be clearer
// - Mixing throws + Result in the same module without a convention

Why it matters

Swift error handling is enum + throws + do / try / catch. Define Error enums per domain, conform to LocalizedError for UI, lean on try? where you do not need the cause, and defer for cleanup. async / await composes cleanly with throws. The patterns are concise once the conventions are in place.

Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.

Example

Example
enum LoginError: Error { case badPassword, locked }

func login(_ pw: String) throws -> String {
    if pw == "" { throw LoginError.badPassword }
    return "token"
}

do {
    let token = try login("hunter2")
    print(token)
} catch {
    print("failed: \(error)")
}
Try it Yourself »

Discussion

Loading…