Skip to content

Commit e053bf6

Browse files
committed
Improve the phrasing of the throwable README section
1 parent 4cfbcff commit e053bf6

File tree

1 file changed

+37
-23
lines changed

1 file changed

+37
-23
lines changed

README.md

Lines changed: 37 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,18 @@
11
# ErrorKit
22

3-
ErrorKit makes error handling in Swift more intuitive. It reduces boilerplate while providing more insights. Helpful for users, fun for developers!
3+
**ErrorKit** makes error handling in Swift more intuitive. It reduces boilerplate code while providing clearer insights. Helpful for users, fun for developers!
44

5-
TODO: list all the advantages when using ErrorKit over Swift's native types
5+
*TODO: Add a list of advantages of using ErrorKit over Swifts native error handling types.*
66

7-
## Why we are introducing the `Throwable` protocol to replace `Error`
7+
---
88

9-
### Confusing `Error` API
9+
## Why We Introduced the `Throwable` Protocol to Replace `Error`
1010

11-
The `Error` type in Swift is a very simple protocol that has no requirements. But it has exactly one computed property we can call named `localizedDescription` which returns a text we can log or show users.
11+
### The Confusing `Error` API
1212

13-
You might have written something like this, providing a `localizedDescription`:
13+
Swift's `Error` protocol is simple – too simple. It has no requirements, but it offers one computed property, `localizedDescription`, which is often used to log errors or display messages to users.
14+
15+
Consider the following example where we provide a `localizedDescription` for an enum:
1416

1517
```swift
1618
enum NetworkError: Error, CaseIterable {
@@ -19,17 +21,14 @@ enum NetworkError: Error, CaseIterable {
1921

2022
var localizedDescription: String {
2123
switch self {
22-
case .noConnectionToServer:
23-
return "No Connection to Server Established"
24-
25-
case .parsingFailed:
26-
return "Parsing Failed"
24+
case .noConnectionToServer: "No connection to the server."
25+
case .parsingFailed: "Data parsing failed."
2726
}
2827
}
2928
}
3029
```
3130

32-
But actually, this doesn't work. If we randomly throw an error and print it's `localizedDescription` like in this view:
31+
You might expect this to work seamlessly, but it doesn’t. If we randomly throw an error and print its `localizedDescription`, like in the following SwiftUI view:
3332

3433
```swift
3534
struct ContentView: View {
@@ -45,29 +44,40 @@ struct ContentView: View {
4544
}
4645
```
4746

48-
The console output is cryptic and not at all what we would expect: 😱
47+
The console output will surprise you: 😱
4948

5049
```bash
5150
Caught error with message: The operation couldn’t be completed. (ErrorKitDemo.NetworkError error 0.)
5251
```
5352

54-
There is zero info about what error case was thrown – heck, not even the enum case name was provided, let alone our provided message! Why is that? That's because the Swift `Error` type is actually bridged to `NSError` which works entirely differently (with `domain`, `code`, and `userInfo`).
53+
There’s no information about the specific error case. Not even the enum case name appears, let alone the custom message! Why? Because Swift’s `Error` protocol is bridged to `NSError`, which uses `domain`, `code`, and `userInfo` instead.
54+
55+
### The "Correct" Way: `LocalizedError`
56+
57+
The correct approach is to conform to `LocalizedError`, which defines the following optional properties:
5558

56-
The correct way in Swift to provide your own error type is actually to conform to `LocalizedError`! It has the following requirements: `errorDescription: String?`, `failureReason: String?`, `recoverySuggestion: String?`, and `helpAnchor: String?`.
59+
- `errorDescription: String?
60+
- `failureReason: String?`
61+
- `recoverySuggestion: String?`
62+
- `helpAnchor: String?`
5763

58-
But all of these are optional, so you won't get any build errors when writing your own error types, making it easy to forget providing a user-friendly message. And the only field that is being used for `localizedDescription` actually is `errorDescription`, the failure reason or recovery suggestions, for example, get completely ignored. And the help anchor is a legacy leftover from old macOS error dialogs, it's very uncommon nowadays.
64+
However, since all of these properties are optional, you wont get any compiler errors if you forget to implement them. Worse, only `errorDescription` affects `localizedDescription`. Fields like `failureReason` and `recoverySuggestion` are ignored, while `helpAnchor` is rarely used today.
5965

60-
All this makes `LocalizedError` confusing and unsafe to use. Which is why we provide our own protocol:
66+
This makes `LocalizedError` both confusing and error-prone.
67+
68+
### The Solution: `Throwable`
69+
70+
To address these issues, **ErrorKit** introduces the `Throwable` protocol:
6171

6272
```swift
6373
public protocol Throwable: LocalizedError {
6474
var localizedDescription: String { get }
6575
}
6676
```
6777

68-
It is super simple and clear. We named it `Throwable` which is consistent with the `throw` keyword and has the common `able` ending used for protocols in Swift (like `Codable`, `Identifiable` and many others). And it actually requires a field and that field is named exactly like the `localizedDescription` we call when catching errors in Swift, making errors intuitive to write.
78+
This protocol is simple and clear. It’s named `Throwable` to align with Swift’s `throw` keyword and follows Swift’s convention of using the `able` suffix (like `Codable` and `Identifiable`). Most importantly, it requires the `localizedDescription` property, ensuring your errors behave exactly as expected.
6979

70-
With this we can simply write:
80+
Here’s how you use it:
7181

7282
```swift
7383
enum NetworkError: Throwable, CaseIterable {
@@ -83,9 +93,11 @@ enum NetworkError: Throwable, CaseIterable {
8393
}
8494
```
8595

86-
Now when printing `error.localizedDescription` we get exactly the message we expect! 🥳
96+
When you print `error.localizedDescription`, you'll get exactly the message you expect! 🥳
8797

88-
But it doesn't end there. We know that not all apps are localized, and not all developer have the time to localize all their errors right away. So we even provide a shorter version that you can use in your first iteration if your cases have no parameters. Just provide raw values like so, making your error type definition even shorter while maintaining descriptive error message:
98+
### Even Shorter Error Definitions
99+
100+
Not all apps are localized, and developers may not have time to provide localized descriptions immediately. To make error handling even simpler, `Throwable` allows you to define your error messages using raw values:
89101

90102
```swift
91103
enum NetworkError: String, Throwable, CaseIterable {
@@ -94,6 +106,8 @@ enum NetworkError: String, Throwable, CaseIterable {
94106
}
95107
```
96108

97-
Section summary:
109+
This approach eliminates boilerplate code while keeping the error definitions concise and descriptive.
110+
111+
### Summary
98112

99-
> We recommend conforming all your custom error types to `Throwable` rather than `Error` or `LocalizedError`. It has one requirement, `localizedDescription: String`, which will be exactly what you expect it to be.
113+
> Conform your custom error types to `Throwable` instead of `Error` or `LocalizedError`. The `Throwable` protocol requires only `localizedDescription: String`, ensuring your error messages are exactly what you expect – no surprises.

0 commit comments

Comments
 (0)