Swift Error Handling: Throwing, Catching, and Propagating
Swift error handling uses a dedicated channel for failures. Functions declare they can fail with `throws`, you handle them with `do-catch`, or transform them into optionals with `try?`. The footgun is overusing `try!`, which crashes your app on failure.
WHY IT EXISTS: Before Swift, error handling in Objective-C often relied on special return values like nil or passing in a pointer to an error object. This was easy to forget to check, mixing success and failure logic. Swift introduced a first-class, explicit system to make error handling safer and clearer.
THE MENTAL MODEL: Think of a function as having two distinct return paths: a normal one for the success value and a separate, dedicated "error" path. A function that can use this error path is marked with the throws keyword. When an error is thrown, normal execution halts, and Swift unwinds the call stack, looking for the nearest catch block that can handle that specific error.
HOW IT WORKS: The system uses four main keywords. First, you define custom error types by creating an enum or struct that conforms to the Error protocol. Second, a function that can produce an error declares this with throws in its signature. Third, inside the function, you use throw to signal a failure. Finally, at the call site, you wrap the call with try inside a do block and handle potential errors in one or more catch blocks.
WHEN TO USE IT: Use Swift's error handling for recoverable errors where the caller needs context about what went wrong. This is ideal for operations like parsing data (e.g., "invalid JSON"), network requests (e.g., "no internet connection"), or file I/O (e.g., "file not found").
WHEN NOT TO USE IT: For simple binary success/failure conditions where no extra context is needed, returning an optional can be simpler (e.g., finding an item in an array). For unrecoverable programmer mistakes, use fatalError() or preconditionFailure() to crash immediately and highlight the bug during development.
ONE CANONICAL EXAMPLE: A function to create a user profile might fail if the username is too short.
enum ProfileError: Error { case usernameTooShort }
func createProfile(username: String) throws { guard username.count > 3 else { throw ProfileError.usernameTooShort } // ... logic to create profile ... print("Profile created!") }
do { try createProfile(username: "Al") } catch ProfileError.usernameTooShort { print("Username must be longer than 3 characters.") } catch { print("An unknown error occurred: \(error)") }
Read the original → docs.swift.org
Get five bites like this every day.
Tezvyn delivers a daily feed of 60-second tech bites with quizzes to lock in what you learn.