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.