Module 10: Error Handling in Swift – do-try-catch, Throwing Functions & More
Error handling is a crucial part of writing robust and safe software. In Swift, error handling enables you to anticipate, catch, and respond to runtime errors gracefully, instead of your program crashing unexpectedly. Swift uses a clean and expressive syntax based on do-try-catch
blocks to handle errors. This module will guide you through the essentials of Swift error handling, including throwing errors, handling them with do-try-catch
, and the optional error handling shortcuts like try?
and try!
.
What is Error Handling?
In Swift, errors are values of types that conform to the Error
protocol. Functions or methods can throw errors when something unexpected happens, such as a file not being found or invalid user input. Instead of returning a failure code, Swift uses throwing functions to propagate errors up the call stack.
Defining and Throwing Errors
To start, you create an enum that conforms to the Error
protocol. Each case in the enum represents a specific error.
Example:
enum FileError: Error {
case fileNotFound
case unreadable
case encodingFailed
}
Then, a function that can throw errors is marked with the throws
keyword:
Example:
func readFile(named fileName: String) throws -> String {
guard fileName == “file.txt” else {
throw FileError.fileNotFound
}
// Simulate file content reading
return “File content here”
}
Using do-try-catch Blocks
When you call a throwing function, you use the try
keyword. To handle possible errors, you wrap the call in a do
block with catch
clauses:
Example:
do {
let content = try readFile(named: “data.txt”)
print(content)
} catch FileError.fileNotFound {
print(“Error: The file was not found.”)
} catch {
print(“An unexpected error occurred: \(error)”)
}
– If readFile
throws an error, execution jumps to the matching catch
block.
– If no error occurs, the file content is printed normally.
Using try? and try!
Swift provides shorthand ways to handle errors when you want to ignore them or are sure they won’t occur:
try?
converts the result into an optional. If an error occurs, the result isnil
.
Example:
let content = try? readFile(named: “file.txt”)
print(content ?? “No content”)
try!
forces the call and crashes if an error is thrown. Use it only when you’re certain no error will occur.
Example:
let content = try! readFile(named: “file.txt”)
print(content)
Throwing Functions with Multiple Error Types
You can define multiple error cases and catch them individually for granular error handling:
Example:
enum NetworkError: Error {
case disconnected
case timeout
case badResponse
}
func fetchData() throws {
throw NetworkError.timeout
}
do {
try fetchData()
} catch NetworkError.timeout {
print(“Request timed out, please try again.”)
} catch NetworkError.disconnected {
print(“No internet connection.”)
} catch {
print(“Unknown network error”)
}
Rethrowing Functions
Sometimes you write functions that call throwing functions but don’t throw errors themselves. You can declare them as rethrows
to propagate the error only if the called function throws:
Example:
func perform(operation: () throws -> Void) rethrows {
try operation()
}
do {
try perform {
throw FileError.unreadable
}
} catch {
print(“Caught error from operation: \(error)”)
}
Summary
- Swift’s error handling uses the
Error
protocol,throws
keyword, anddo-try-catch
blocks. - You define errors with enums conforming to
Error
. - Use
try?
andtry!
for simplified error handling when appropriate. - Catch specific errors for fine-grained control.
- Use
rethrows
for functions that propagate errors from function parameters.
Error handling is essential for writing safe, predictable Swift code, especially when working with file I/O, networking, or user input validation.