Error Handling (do/try/catch)
Real programs fail — networks drop, input is bad, files go missing. Swift handles this cleanly with the Error protocol, throws functions, and do/try/catch . You'll also meet try? and try! .
Learn Error Handling (do/try/catch) in our free Swift course — a beginner-friendly interactive lesson with worked examples, a practice exercise and a quick…
Part of the free Swift course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.
What You'll Learn in This Lesson
1️⃣ Errors, throws & do/try/catch
Define an error type (usually an enum conforming to Error ), mark a failing function throws , raise errors with throw , and call it inside a do block with try .
2️⃣ Matching Specific Errors
Multiple catch clauses let you respond differently to each kind of failure, from most specific to a final general catch .
3️⃣ try? and try!
Sometimes you don't need a full do/catch . try? turns a throwing call into an optional (value or nil ). try! assumes success and crashes on error — reserve it for cases where failure is impossible.
Your turn. Fill in the blanks to throw and then call safely with try? .
📋 Quick Reference
No blanks this time — just a brief and an outline. Build it, run it, and check your output against the example in the comments.
Practice quiz
What protocol must a Swift error type adopt?
- Codable
- Throwable
- Error
- Failure
Answer: Error. Custom error types conform to the Error protocol — commonly an enum that adopts Error.
Which keyword marks a function that can throw an error?
- throws
- throwing
- error
- risky
Answer: throws. A function that can throw is declared with 'throws' before the return arrow: func f() throws -> T.
How do you actually raise an error inside a function?
- raise error
- return error
- error()
- throw someError
Answer: throw someError. You raise an error with the 'throw' keyword: throw MyError.badInput.
Which block structure handles a throwing call?
- if/else
- do/try/catch
- guard/let
- switch/case
Answer: do/try/catch. You call throwing code inside a 'do' block with 'try', and handle failures in 'catch'.
What keyword precedes a throwing call inside a do block?
- try
- catch
- throws
- do
Answer: try. Each call that can throw must be marked with 'try'.
What does 'try?' do?
- Crashes on error
- Ignores the function
- Converts the result to an optional — nil on error
- Re-throws the error
Answer: Converts the result to an optional — nil on error. try? turns a throwing call into an optional: you get the value, or nil if it threw.
What does 'try!' do?
- Returns an optional
- Force-unwraps — crashes if the call throws
- Catches the error silently
- Retries the call
Answer: Force-unwraps — crashes if the call throws. try! assumes success; if the call throws, the program crashes — use only when failure is impossible.
After a successful 'try?', what is the result's type for a function returning Int?
- Int
- Error
- Bool
- Int?
Answer: Int?. try? wraps the return value in an optional, so a throwing -> Int becomes Int? at the call site.
Can a catch block bind the specific error that was thrown?
- No
- Yes — catch let error, or pattern-match specific cases
- Only with try!
- Only in protocols
Answer: Yes — catch let error, or pattern-match specific cases. catch can pattern-match specific cases or bind the error, e.g. catch MyError.badInput or catch let e.
Why does Swift require 'try' on every throwing call?
- For performance
- To allow nil
- To make potential failure points visible in the code
- It's optional styling
Answer: To make potential failure points visible in the code. Marking each throwing call with try makes the points where errors can occur explicit and visible.