Error Handling
## Learning Objectives
- Understand Swift error handling
- Throw and catch errors
- Use Result type
- Handle errors gracefully
## Error Protocol
### Defining Errors
```swift
enum ValidationError: Error {
case empty
case tooShort
case invalidCharacter
}
```
### Custom Error Types
```swift
enum NetworkError: Error {
case badURL
case noConnection
case timeout
case invalidResponse
case serverError(Int)
}
```
## Throwing Errors
### Using throw
```swift
func validate(name: String?) throws {
guard let name = name else {
throw ValidationError.empty
}
guard name.count >= 2 else {
throw ValidationError.tooShort
}
guard name.allSatisfy({ $0.isLetter }) else {
throw ValidationError.invalidCharacter
}
}
```
### Throwing Functions
```swift
func canThrow() throws -> String {
// Function that can throw
throw SomeError.oops
return "Success"
}
// vs
func cannotThrow() -> String {
// Function that cannot throw
return "Success"
}
```
## Try Expression
### Basic try
```swift
do {
let result = try validate(name: "Alice")
print("Valid: \(result)")
} catch {
print("Error occurred")
}
```
### try? (Optional Try)
Returns nil on error:
```swift
let result = try? validate(name: "A")
// result is nil if error thrown
if let valid = try? validate(name: "Alice") {
print(valid)
}
```
### try! (Force Try)
Crashes on error:
```swift
let result = try! validate(name: "Alice")
// CRASHES if error thrown!
```
## Catch Clauses
### Basic Catch
```swift
do {
try validate(name: "")
} catch {
print("Validation failed")
}
```
### Catch Specific Errors
```swift
do {
try validate(name: "")
} catch ValidationError.empty {
print("Name cannot be empty")
} catch ValidationError.tooShort {
print("Name is too short")
} catch ValidationError.invalidCharacter {
print("Name contains invalid characters")
}
```
### Catch with Pattern
```swift
do {
try fetchData(from: "invalid")
} catch NetworkError.serverError(let code) {
print("Server error with code: \(code)")
} catch NetworkError.timeout {
print("Request timed out")
} catch {
print("Unknown error: \(error)")
}
```
### Catch Any Error
```swift
do {
try riskyOperation()
} catch let error as MyError {
print("Caught specific error: \(error)")
} catch {
print("Caught any error: \(error)")
}
```
## Error Propagation
### Propagating Errors
```swift
func processUserInput(_ input: String?) throws {
guard let name = input else {
throw ValidationError.empty
}
try validate(name: name) // Errors propagate up
}
do {
try processUserInput(nil)
} catch {
print("Handled at top level: \(error)")
}
```
### Rethrowing
```swift
func handleErrors(_ operation: () throws -> Void) rethrows {
do {
try operation()
} catch {
print("Handled: \(error)")
}
}
```
## Result Type
### Result Enum
```swift
enum Result {
case success(Success)
case failure(Failure)
}
```
### Using Result
```swift
func fetchUser(id: Int) -> Result {
if id > 0 {
return .success(User(id: id, name: "Alice"))
} else {
return .failure(NetworkError.invalidResponse)
}
}
```
### Handling Result
```swift
let result = fetchUser(id: 1)
switch result {
case .success(let user):
print("Got user: \(user.name)")
case .failure(let error):
print("Error: \(error)")
}
```
### Result with Optional
```swift
let result = Result.success(User(id: 1, name: "Alice"))
// Never means cannot fail
```
### get() Method
```swift
let success = Result.success(42)
let failure = Result.failure(NetworkError.timeout)
try? success.get() // Optional(42)
try? failure.get() // nil
```
## Deferred Execution
### defer Statement
```swift
func readFile() {
let file = openFile("data.txt")
defer {
closeFile(file) // Always executed before return
}
// Work with file
let content = readContent(file)
// File automatically closed here
}
```
### Multiple defers
```swift
func example() {
defer { print("1") }
defer { print("2") }
defer { print("3") }
}
// Output: 3, 2, 1 (LIFO order)
```
### defer with Error Handling
```swift
func process() throws {
let resource = acquireResource()
defer {
releaseResource(resource) // Always released
}
try doWork(resource)
// resource released whether success or failure
}
```
## Custom Error Handling
### Error Conformance
```swift
struct CustomError: Error {
let message: String
}
throw CustomError(message: "Something went wrong")
```
### LocalizedError
```swift
struct FileNotFoundError: Error, LocalizedError {
let filename: String
var errorDescription: String? {
return "File not found: \(filename)"
}
}
```
### Custom NSError
```swift
extension CustomError: CustomNSError {
var errorCode: Int {
return 42
}
var errorUserInfo: [String: Any] {
return ["customKey": "customValue"]
}
}
```
## Best Practices
### Do
```swift
// Use specific error types
enum AppError: Error {
case invalidInput
case networkFailure
case notFound
}
// Propagate errors when appropriate
func loadData() throws -> Data {
let url = try getURL()
return try fetch(from: url)
}
// Use Result for async callbacks
completion(.success(data))
completion(.failure(error))
```
### Don't
```swift
// Don't swallow errors silently
do {
try operation()
} catch {
// BAD: Doing nothing hides bugs!
}
// Don't use try! without justification
let value = try! parse(json) // Only if you're certain
```
## Common Patterns
### Error Handling with Optionals
```swift
// Returns nil instead of throwing
func parseSafely(_ string: String) -> Int? {
return Int(string)
}
let number = parseSafely("42") ?? 0
```
### Failed Optional Chaining
```swift
struct User {
let name: String
}
func getUser() throws -> User { ... }
let name = try? getUser()?.name
// name is nil if getUser() returns nil or throws
```
### Transforming Errors
```swift
enum AppError: Error {
case invalidEmail
}
func validate(email: String) throws {
guard email.contains("@") else {
throw AppError.invalidEmail
}
}
let email: String? = "test@example.com"
try? email.flatMap { try? validate(email: $0) }
// Flattens optional and error handling
```
## Summary
- Errors conform to `Error` protocol
- Throwing functions marked with `throws`
- Use `do-catch` to handle errors
- `try?` returns nil on error
- `try!` crashes on error (use sparingly)
- `Result` for error-returning operations
- `defer` for cleanup code
- Propagate errors with `rethrows`
- Always handle errors appropriately
Comments
Comments powered by Giscus
To enable comments, add your Giscus embed code here.
Learn more about Giscus →