Skip to content
Error Handling
step 1/7

Reading — step 1 of 7

Learn

~3 min readTypes, Enums, and Errors

Swift's error handling is typed and explicit — functions that throw declare it; callers must use try (or try? / try!); errors are values that conform to the Error protocol.

Defining errors

Any type conforming to Error can be thrown. Enums with associated values are ideal:

enum ParseError: Error {
    case empty
    case notANumber(String)
    case negative(Int)
}

The Error protocol has no required methods — it's a marker.

Throwing functions

Mark with throws:

func parseAge(_ s: String) throws -> Int {
    if s.isEmpty { throw ParseError.empty }
    guard let n = Int(s) else { throw ParseError.notANumber(s) }
    if n < 0 { throw ParseError.negative(n) }
    return n
}

Callers MUST handle the possibility of failure — Swift won't let you ignore it.

try, try?, try!

Three ways to call a throwing function:

// 1. try inside do/catch — handle errors
do {
    let age = try parseAge("42")
    print(age)
} catch ParseError.empty {
    print("empty input")
} catch ParseError.notANumber(let s) {
    print("not a number: \(s)")
} catch ParseError.negative(let n) {
    print("negative: \(n)")
} catch {
    print("other error: \(error)")
}

// 2. try? — convert to optional (nil on error)
let age: Int? = try? parseAge("42")

// 3. try! — assert success (CRASH on error)
let definitelyValid = try! parseAge("42")

Use:

  • try + do/catch when you want to handle the error
  • try? when you don't care WHY it failed, just whether it did
  • try! only when failure is genuinely impossible (constants, hardcoded values)

Pattern matching errors

The catch clause is a pattern — match specific cases:

do {
    try doWork()
} catch ParseError.notANumber(let s) where s.hasPrefix("#") {
    print("hex format not supported: \(s)")
} catch let error as ParseError {
    print("parse error: \(error)")
} catch {
    print("unknown: \(error)")
}

error is automatically bound in the default catch.

Propagating errors

A throwing function can let errors bubble up by simply marking itself throws:

func loadConfig(_ path: String) throws -> Config {
    let text = try readFile(path)        // readFile throws — propagates
    return try parseConfig(text)         // parseConfig throws — propagates
}

No need to do/catchtry without surrounding catch propagates. Cleaner than wrapping every call.

defer — guaranteed cleanup

func processFile(_ path: String) throws {
    let file = try openFile(path)
    defer {
        file.close()
    }
    // process...
}

defer runs when the function exits, by ANY path — return, throw, or fallthrough. Like Go's defer or Java's try/finally.

rethrows

A function that calls a throwing closure can mark itself rethrows — only throws if the closure throws:

func transform<T>(_ items: [Int], using f: (Int) throws -> T) rethrows -> [T] {
    return try items.map(f)
}

// Doesn't need try if f doesn't throw:
transform([1, 2, 3]) { $0 * 2 }            // fine, no try

// Does if f throws:
try transform([1, 2, 3]) { try parseAge("\($0)") }

Common for higher-order functions like map, filter, forEach.

When to use error throwing vs Result

Swift has both throws and Result<Success, Failure>. Use:

  • throws for the common case — synchronous, straightforward control flow
  • Result when you need to STORE the result for later (e.g., async callbacks, queues), or when you want to compose with map/flatMap chain

They convert easily: Result(catching: { try parseAge(s) }) and try result.get().

Common mistakes

  • Using try! reflexively — turns recoverable errors into crashes. Use only for true invariants.
  • Catching Error as a wide net — loses type information. Match specific cases when you can react meaningfully.
  • Forgetting that throws is part of the function type — closures that throw need throws in their type signature.
  • Throwing String — throw "oops" — String doesn't conform to Error. Define an enum or use NSError.
  • Mixing thrown errors with Optional-returns — pick one. throws conveys 'why it failed'; optional says 'it failed or didn't'.

Discussion

Ask a question, share an insight, or help someone who’s stuck.

Sign in to post a comment or reply.

Loading…