Lesson 81Error Handling
Error Types
Welcome to Error Handling!
Error handling is crucial for building robust applications. Swift provides a powerful, type-safe error handling system that helps you write code that gracefully handles failures.
The Error Protocol
Any type that conforms to the Error protocol can be used as an error. Most commonly, we use enums with associated values to define clear, descriptive errors.
Ways to Define Errors
Enum (Most Common)
Cases represent different error types
Associated Values
Attach context to each error case
Raw Values
Map errors to codes (Int, String)
Struct/Class
Complex errors with multiple properties
LocalizedError Protocol
Conform to LocalizedError to provide user-friendly error messages:
errorDescription- Main error messagefailureReason- Why the error occurredrecoverySuggestion- How to fix it
Best Practices
- Use enums for most error definitions
- Group related errors in the same enum
- Use associated values for context
- Implement LocalizedError for user-facing errors
- Name errors descriptively (e.g., NetworkError, ValidationError)
main.swift
// ERROR TYPES IN SWIFT
// Swift uses the Error protocol to represent errors
// DEFINING CUSTOM ERRORS WITH ENUM
enum NetworkError: Error {
case noConnection
case timeout
case serverError(code: Int)
case invalidURL
case unauthorized
}
enum FileError: Error {
case notFound
case permissionDenied
case corrupted
case diskFull
}
enum ValidationError: Error {
case emptyField(fieldName: String)
case invalidEmail
case passwordTooShort(minimum: Int)
case passwordMismatch
}
// ERRORS WITH ASSOCIATED VALUES
enum APIError: Error {
case badRequest(message: String)
case notFound(resource: String)
case serverError(statusCode: Int, body: String)
case decodingFailed(type: String)
}
// EXAMPLE: Using associated values
let error = APIError.serverError(statusCode: 500, body: "Internal error")
// ERRORS WITH RAW VALUES
enum HTTPError: Int, Error {
case badRequest = 400
case unauthorized = 401
case forbidden = 403
case notFound = 404
case serverError = 500
}
let httpError = HTTPError.notFound
print("Status code: \(httpError.rawValue)") // 404
// CONFORMING TO LocalizedError
enum LoginError: Error, LocalizedError {
case invalidCredentials
case accountLocked
case networkUnavailable
var errorDescription: String? {
switch self {
case .invalidCredentials:
return "Invalid username or password"
case .accountLocked:
return "Your account has been locked"
case .networkUnavailable:
return "No internet connection"
}
}
var recoverySuggestion: String? {
switch self {
case .invalidCredentials:
return "Please check your credentials and try again"
case .accountLocked:
return "Contact support to unlock your account"
case .networkUnavailable:
return "Check your network settings"
}
}
}
// Using LocalizedError
let loginError: LoginError = .invalidCredentials
print(loginError.localizedDescription)
// "Invalid username or password"
// STRUCT-BASED ERRORS
struct CustomError: Error {
let code: Int
let message: String
let timestamp: Date
init(code: Int, message: String) {
self.code = code
self.message = message
self.timestamp = Date()
}
}
let customErr = CustomError(code: 100, message: "Something went wrong")
// ERROR PROTOCOL REQUIREMENTS
// The Error protocol is empty - any type can conform!
// protocol Error { }
// COMPARING ERRORS
enum AppError: Error, Equatable {
case unknown
case network
case database
}
let err1 = AppError.network
let err2 = AppError.network
print(err1 == err2) // trueTry It Yourself!
Create a PaymentError enum with cases for insufficientFunds, cardDeclined, and expiredCard. Add associated values and conform to LocalizedError!