Skip to content

Commit 17843c3

Browse files
committed
Apply improved messages for error string interpolation, add chain option
1 parent 2c1aa89 commit 17843c3

File tree

3 files changed

+75
-1
lines changed

3 files changed

+75
-1
lines changed

Sources/ErrorKit/ErrorKit.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ public enum ErrorKit {
168168
} else {
169169
// For enums, include the full case description with type name
170170
if let enclosingType {
171-
return "\(enclosingType).\(error)"
171+
return "\(enclosingType).\(String(describing: error))"
172172
} else {
173173
return String(describing: error)
174174
}

Sources/ErrorKit/Helpers/String+ErrorKit.swift

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,38 @@ extension String {
1717
}
1818
#endif
1919
}
20+
21+
extension String.StringInterpolation {
22+
/// Interpolates an error using its user-friendly message.
23+
///
24+
/// Uses ``ErrorKit.userFriendlyMessage(for:)`` to provide clear, actionable error descriptions
25+
/// suitable for displaying to users. For nested errors, returns the message from the root cause.
26+
///
27+
/// ```swift
28+
/// showAlert("Operation failed: \(error)")
29+
/// ```
30+
///
31+
/// - Parameter error: The error to interpolate using its user-friendly message
32+
mutating public func appendInterpolation(_ error: some Error) {
33+
self.appendInterpolation(ErrorKit.userFriendlyMessage(for: error))
34+
}
35+
36+
/// Interpolates an error using its complete chain description for debugging.
37+
///
38+
/// Uses ``ErrorKit.errorChainDescription(for:)`` to show the full error hierarchy,
39+
/// type information, and nested structure. Ideal for logging and debugging.
40+
///
41+
/// ```swift
42+
/// Logger().error("Operation failed with:\n\(chain: error)")
43+
/// // Operation failed with:
44+
/// // DatabaseError
45+
/// // └─ FileError
46+
/// // └─ PermissionError.denied(permission: "~/Downloads/Profile.png")
47+
/// // └─ userFriendlyMessage: "Access to ~/Downloads/Profile.png was declined..."
48+
/// ```
49+
///
50+
/// - Parameter error: The error to interpolate using its complete chain description
51+
mutating public func appendInterpolation(chain error: some Error) {
52+
self.appendInterpolation(ErrorKit.errorChainDescription(for: error))
53+
}
54+
}

Tests/ErrorKitTests/ErrorKitTests.swift

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,45 @@ enum ErrorKitTests {
4040
}
4141
}
4242

43+
enum StringInterpolation {
44+
@Test
45+
static func implicitWithStruct() async throws {
46+
#expect("\(SomeThrowable())" == "Something failed hard.")
47+
}
48+
49+
@Test
50+
static func implicitWithNestedError() async throws {
51+
let nestedError = DatabaseError.caught(FileError.caught(PermissionError.denied(permission: "~/Downloads/Profile.png")))
52+
#expect("\(nestedError)" == "Access to ~/Downloads/Profile.png was declined. To use this feature, please enable the permission in your device Settings.")
53+
}
54+
55+
@Test
56+
static func chainWithStruct() async throws {
57+
#expect(
58+
"\(chain: SomeThrowable())"
59+
==
60+
"""
61+
SomeThrowable [Struct]
62+
└─ userFriendlyMessage: "Something failed hard."
63+
"""
64+
)
65+
}
66+
67+
@Test
68+
static func chainWithNestedError() async throws {
69+
let nestedError = DatabaseError.caught(FileError.caught(PermissionError.denied(permission: "~/Downloads/Profile.png")))
70+
#expect(
71+
"\(chain: nestedError)"
72+
==
73+
"""
74+
DatabaseError
75+
└─ FileError
76+
└─ PermissionError.denied(permission: "~/Downloads/Profile.png")
77+
└─ userFriendlyMessage: "Access to ~/Downloads/Profile.png was declined. To use this feature, please enable the permission in your device Settings."
78+
""")
79+
}
80+
}
81+
4382
enum ErrorChainDescription {
4483
@Test
4584
static func localizedError() {

0 commit comments

Comments
 (0)