You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: README.md
+128Lines changed: 128 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -293,3 +293,131 @@ When contributing:
293
293
- Follow the existing naming conventions
294
294
295
295
Together, we can build a comprehensive set of error types that cover most common scenarios in Swift development and create a more unified error handling experience across the ecosystem.
296
+
297
+
298
+
## Simplified Error Nesting with the `Catching` Protocol
299
+
300
+
ErrorKit's `Catching` protocol simplifies error handling in modular applications by providing an elegant way to handle nested error hierarchies. It eliminates the need for explicit wrapper cases while maintaining type safety through typed throws.
301
+
302
+
### The Problem with Manual Error Wrapping
303
+
304
+
In modular applications, errors often need to be propagated up through multiple layers. The traditional approach requires defining explicit wrapper cases for each possible error type:
305
+
306
+
```swift
307
+
enumProfileError: Error{
308
+
casevalidationFailed(field: String)
309
+
casedatabaseError(DatabaseError) // Wrapper case needed
310
+
casenetworkError(NetworkError) // Another wrapper case
311
+
casefileError(FileError) // Yet another wrapper
312
+
}
313
+
314
+
// And manual error wrapping in code:
315
+
do {
316
+
try database.fetch(id)
317
+
} catchlet error asDatabaseError {
318
+
throw .databaseError(error)
319
+
}
320
+
```
321
+
322
+
This leads to verbose error types and tedious error handling code when attempting to use typed throws.
323
+
324
+
### The Solution: `Catching` Protocol
325
+
326
+
ErrorKit's `Catching` protocol provides a single `caught` case that can wrap any error, plus a convenient `catch` function for automatic error wrapping:
327
+
328
+
```swift
329
+
enumProfileError: Throwable, Catching {
330
+
casevalidationFailed(field: String)
331
+
casecaught(Error) // Single case handles all nested errors!
let settings =try fileSystem.readUserSettings(user.settingsPath)
345
+
returnUserProfile(user: user, settings: settings)
346
+
}
347
+
}
348
+
}
349
+
```
350
+
351
+
Note the `ProfileError.catch` function call, which wraps any errors into the `caught` case and also passes through the return type.
352
+
353
+
### Built-in Support in ErrorKit Types
354
+
355
+
All of ErrorKit's built-in error types (`DatabaseError`, `FileError`, `NetworkError`, etc.) already conform to `Catching`, allowing you to easily wrap system errors or other error types:
356
+
357
+
```swift
358
+
funcsaveUserData() throws(DatabaseError) {
359
+
// Automatically wraps SQLite errors, file system errors, etc.
360
+
try DatabaseError.catch {
361
+
try database.beginTransaction()
362
+
try database.execute(query)
363
+
try database.commit()
364
+
}
365
+
}
366
+
```
367
+
368
+
### Adding Catching to Your Error Types
369
+
370
+
Making your own error types support automatic error wrapping is simple:
371
+
372
+
1. Conform to the `Catching` protocol
373
+
2. Add the `caught(Error)` case to your error type
374
+
3. Use the `catch` function for automatic wrapping
375
+
376
+
```swift
377
+
enumAppError: Throwable, Catching {
378
+
caseinvalidConfiguration
379
+
casecaught(Error) // Required for Catching protocol
380
+
381
+
var userFriendlyMessage: String {
382
+
switchself {
383
+
case .invalidConfiguration:
384
+
returnString(localized: "The app configuration is invalid.")
385
+
case .caught(let error):
386
+
return error.localizedDescription
387
+
}
388
+
}
389
+
}
390
+
391
+
// Usage is clean and simple:
392
+
funcappOperation() throws(AppError) {
393
+
// Explicit error throwing for known cases
394
+
guard configFileExists else {
395
+
throw AppError.invalidConfiguration
396
+
}
397
+
398
+
// Automatic wrapping for system errors and other error types
399
+
try AppError.catch {
400
+
tryriskyOperation()
401
+
tryanotherRiskyOperation()
402
+
}
403
+
}
404
+
```
405
+
406
+
### Benefits of Using `Catching`
407
+
408
+
-**Less Boilerplate**: No need for explicit wrapper cases for each error type
409
+
-**Type Safety**: Maintains typed throws while simplifying error handling
410
+
-**Clean Code**: Reduces error handling verbosity
411
+
-**Automatic Message Propagation**: User-friendly messages flow through the error chain
412
+
-**Easy Integration**: Works seamlessly with existing error types
413
+
-**Return Value Support**: The `catch` function preserves return values from wrapped operations
414
+
415
+
### Best Practices
416
+
417
+
- Use `Catching` for error types that might wrap other errors
418
+
- Keep error hierarchies shallow when possible
419
+
- Use specific error cases for known errors, `caught` for others
420
+
- Preserve user-friendly messages when wrapping errors
421
+
- Consider error recovery strategies at each level
422
+
423
+
The `Catching` protocol makes error handling in Swift more intuitive and maintainable, especially in larger applications with complex error hierarchies. Combined with typed throws, it provides a powerful way to handle errors while keeping your code clean and maintainable.
0 commit comments