← Swift EnglishChapter 10 of 13

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 →