Skip to content

Commit 388cdf8

Browse files
committed
Add initial set of common built-in error types for developers to throw
1 parent a5edc71 commit 388cdf8

File tree

8 files changed

+375
-0
lines changed

8 files changed

+375
-0
lines changed
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import Foundation
2+
3+
/// Represents errors that occur during database operations.
4+
public enum DatabaseError: Throwable {
5+
/// The database connection failed.
6+
case connectionFailed
7+
8+
/// The database query failed to execute.
9+
/// - Parameters:
10+
/// - context: A description of the operation or entity being queried.
11+
case operationFailed(context: String)
12+
13+
/// A requested record was not found in the database.
14+
/// - Parameters:
15+
/// - entity: The name of the entity or record type.
16+
/// - identifier: A unique identifier for the missing entity.
17+
case recordNotFound(entity: String, identifier: String?)
18+
19+
/// Generic error message if the existing cases don't provide the required details.
20+
case generic(userFriendlyMessage: String)
21+
22+
/// A user-friendly error message suitable for display to end users.
23+
public var userFriendlyMessage: String {
24+
switch self {
25+
case .connectionFailed:
26+
return String(
27+
localized: "BuiltInErrors.DatabaseError.connectionFailed",
28+
defaultValue: "Failed to connect to the database. Please try again later.",
29+
bundle: .module
30+
)
31+
case .operationFailed(let context):
32+
return String(
33+
localized: "BuiltInErrors.DatabaseError.operationFailed",
34+
defaultValue: "An error occurred while performing the operation: \(context). Please try again.",
35+
bundle: .module
36+
)
37+
case .recordNotFound(let entity, let identifier):
38+
let idMessage = identifier.map { " Identifier: \($0)." } ?? ""
39+
return String(
40+
localized: "BuiltInErrors.DatabaseError.recordNotFound",
41+
defaultValue: "The \(entity) record could not be found.\(idMessage) Please check and try again.",
42+
bundle: .module
43+
)
44+
case .generic(let userFriendlyMessage):
45+
return userFriendlyMessage
46+
}
47+
}
48+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import Foundation
2+
3+
/// Represents errors that occur during file operations.
4+
public enum FileError: Throwable {
5+
/// The file could not be found.
6+
case fileNotFound(fileName: String)
7+
8+
/// There was an issue reading the file.
9+
case readFailed(fileName: String)
10+
11+
/// There was an issue writing to the file.
12+
case writeFailed(fileName: String)
13+
14+
/// Generic error message if the existing cases don't provide the required details.
15+
case generic(userFriendlyMessage: String)
16+
17+
/// A user-friendly error message suitable for display to end users.
18+
public var userFriendlyMessage: String {
19+
switch self {
20+
case .fileNotFound(let fileName):
21+
return String(
22+
localized: "BuiltInErrors.FileError.fileNotFound",
23+
defaultValue: "The file \(fileName) could not be found. Please check the file path.",
24+
bundle: .module
25+
)
26+
case .readFailed(let fileName):
27+
return String(
28+
localized: "BuiltInErrors.FileError.readError",
29+
defaultValue: "There was an issue reading the file \(fileName). Please try again.",
30+
bundle: .module
31+
)
32+
case .writeFailed(let fileName):
33+
return String(
34+
localized: "BuiltInErrors.FileError.writeError",
35+
defaultValue: "There was an issue writing to the file \(fileName). Please try again.",
36+
bundle: .module
37+
)
38+
case .generic(let userFriendlyMessage):
39+
return userFriendlyMessage
40+
}
41+
}
42+
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import Foundation
2+
3+
/// Represents errors that can occur during network operations.
4+
public enum NetworkError: Throwable {
5+
/// No internet connection is available.
6+
/// - Note: This error may occur if the device is in airplane mode or lacks network connectivity.
7+
case noInternet
8+
9+
/// The request timed out before completion.
10+
case timeout
11+
12+
/// The server responded with a bad request error.
13+
/// - Parameters:
14+
/// - code: The exact HTTP status code returned by the server.
15+
/// - message: An error message provided by the server in the body.
16+
case badRequest(code: Int, message: String)
17+
18+
/// The server responded with a general server-side error.
19+
/// - Parameters:
20+
/// - code: The HTTP status code returned by the server.
21+
/// - message: An optional error message provided by the server.
22+
case serverError(code: Int, message: String?)
23+
24+
/// The response could not be decoded or parsed.
25+
case decodingFailure
26+
27+
/// Generic error message if the existing cases don't provide the required details.
28+
case generic(userFriendlyMessage: String)
29+
30+
/// A user-friendly error message suitable for display to end users.
31+
public var userFriendlyMessage: String {
32+
switch self {
33+
case .noInternet:
34+
return String(
35+
localized: "BuiltInErrors.NetworkError.noInternet",
36+
defaultValue: "No internet connection is available. Please check your network settings and try again.",
37+
bundle: .module
38+
)
39+
case .timeout:
40+
return String(
41+
localized: "BuiltInErrors.NetworkError.timeout",
42+
defaultValue: "The request timed out. Please try again later.",
43+
bundle: .module
44+
)
45+
case .badRequest(let code, let message):
46+
return String(
47+
localized: "BuiltInErrors.NetworkError.badRequest",
48+
defaultValue: "The request was malformed (\(code)): \(message). Please review and try again.",
49+
bundle: .module
50+
)
51+
case .serverError(let code, let message):
52+
let defaultMessage = String(
53+
localized: "BuiltInErrors.NetworkError.serverError",
54+
defaultValue: "The server encountered an error (Code: \(code)).",
55+
bundle: .module
56+
)
57+
if let message = message {
58+
return defaultMessage + " " + message
59+
} else {
60+
return defaultMessage
61+
}
62+
case .decodingFailure:
63+
return String(
64+
localized: "BuiltInErrors.NetworkError.decodingFailure",
65+
defaultValue: "The data received from the server could not be processed. Please try again.",
66+
bundle: .module
67+
)
68+
case .generic(let userFriendlyMessage):
69+
return userFriendlyMessage
70+
}
71+
}
72+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import Foundation
2+
3+
/// Represents errors related to failed or invalid operations.
4+
public enum OperationError: Throwable {
5+
/// The operation could not start due to a dependency failure.
6+
/// - Parameter dependency: A description of the failed dependency.
7+
case dependencyFailed(dependency: String)
8+
9+
/// The operation was canceled before completion.
10+
case canceled
11+
12+
/// The operation failed with an unknown reason.
13+
case unknownFailure(description: String)
14+
15+
/// Generic error message if the existing cases don't provide the required details.
16+
case generic(userFriendlyMessage: String)
17+
18+
/// A user-friendly error message suitable for display to end users.
19+
public var userFriendlyMessage: String {
20+
switch self {
21+
case .dependencyFailed(let dependency):
22+
return String(
23+
localized: "BuiltInErrors.OperationError.dependencyFailed",
24+
defaultValue: "The operation could not be completed due to a failed dependency: \(dependency).",
25+
bundle: .module
26+
)
27+
case .canceled:
28+
return String(
29+
localized: "BuiltInErrors.OperationError.canceled",
30+
defaultValue: "The operation was canceled. Please try again if necessary.",
31+
bundle: .module
32+
)
33+
case .unknownFailure(let description):
34+
return String(
35+
localized: "BuiltInErrors.OperationError.unknownFailure",
36+
defaultValue: "The operation failed due to an unknown reason: \(description). Please try again or contact support.",
37+
bundle: .module
38+
)
39+
case .generic(let userFriendlyMessage):
40+
return userFriendlyMessage
41+
}
42+
}
43+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import Foundation
2+
3+
/// Represents errors that occur during parsing of input or data.
4+
public enum ParsingError: Throwable {
5+
/// The input was invalid and could not be parsed.
6+
/// - Parameter input: The invalid input string or description.
7+
case invalidInput(input: String)
8+
9+
/// A required field was missing in the input.
10+
/// - Parameter field: The name of the missing field.
11+
case missingField(field: String)
12+
13+
/// Generic error message if the existing cases don't provide the required details.
14+
case generic(userFriendlyMessage: String)
15+
16+
/// A user-friendly error message suitable for display to end users.
17+
public var userFriendlyMessage: String {
18+
switch self {
19+
case .invalidInput(let input):
20+
return String(
21+
localized: "BuiltInErrors.ParsingError.invalidInput",
22+
defaultValue: "The provided input is invalid: \(input). Please correct it and try again.",
23+
bundle: .module
24+
)
25+
case .missingField(let field):
26+
return String(
27+
localized: "BuiltInErrors.ParsingError.missingField",
28+
defaultValue: "A required field is missing: \(field). Please review and try again.",
29+
bundle: .module
30+
)
31+
case .generic(let userFriendlyMessage):
32+
return userFriendlyMessage
33+
}
34+
}
35+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import Foundation
2+
3+
/// Represents errors related to missing or denied permissions.
4+
public enum PermissionError: Throwable {
5+
/// The user denied the required permission.
6+
/// - Parameter permission: The type of permission that was denied.
7+
case denied(permission: String)
8+
9+
/// The app lacks a required permission and the user cannot grant it.
10+
/// - Parameter permission: The type of permission required.
11+
case restricted(permission: String)
12+
13+
/// The app lacks a required permission, and it is unknown whether the user can grant it.
14+
case notDetermined(permission: String)
15+
16+
/// Generic error message if the existing cases don't provide the required details.
17+
case generic(userFriendlyMessage: String)
18+
19+
/// A user-friendly error message suitable for display to end users.
20+
public var userFriendlyMessage: String {
21+
switch self {
22+
case .denied(let permission):
23+
return String(
24+
localized: "BuiltInErrors.PermissionError.denied",
25+
defaultValue: "The \(permission) permission was denied. Please enable it in Settings to continue.",
26+
bundle: .module
27+
)
28+
case .restricted(let permission):
29+
return String(
30+
localized: "BuiltInErrors.PermissionError.restricted",
31+
defaultValue: "The \(permission) permission is restricted. This may be due to parental controls or other system restrictions.",
32+
bundle: .module
33+
)
34+
case .notDetermined(let permission):
35+
return String(
36+
localized: "BuiltInErrors.PermissionError.notDetermined",
37+
defaultValue: "The \(permission) permission has not been determined. Please try again or check your Settings.",
38+
bundle: .module
39+
)
40+
case .generic(let userFriendlyMessage):
41+
return userFriendlyMessage
42+
}
43+
}
44+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import Foundation
2+
3+
/// Represents errors caused by invalid or unexpected states.
4+
public enum StateError: Throwable {
5+
/// The required state was not met to proceed with the operation.
6+
case invalidState(description: String)
7+
8+
/// The operation cannot proceed because the state has already been finalized.
9+
case alreadyFinalized
10+
11+
/// A required precondition for the operation was not met.
12+
case preconditionFailed(description: String)
13+
14+
/// Generic error message if the existing cases don't provide the required details.
15+
case generic(userFriendlyMessage: String)
16+
17+
/// A user-friendly error message suitable for display to end users.
18+
public var userFriendlyMessage: String {
19+
switch self {
20+
case .invalidState(let description):
21+
return String(
22+
localized: "BuiltInErrors.StateError.invalidState",
23+
defaultValue: "The operation cannot proceed due to an invalid state: \(description).",
24+
bundle: .module
25+
)
26+
case .alreadyFinalized:
27+
return String(
28+
localized: "BuiltInErrors.StateError.alreadyFinalized",
29+
defaultValue: "The operation cannot be performed because the state is already finalized.",
30+
bundle: .module
31+
)
32+
case .preconditionFailed(let description):
33+
return String(
34+
localized: "BuiltInErrors.StateError.preconditionFailed",
35+
defaultValue: "A required condition was not met: \(description). Please review and try again.",
36+
bundle: .module
37+
)
38+
case .generic(let userFriendlyMessage):
39+
return userFriendlyMessage
40+
}
41+
}
42+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import Foundation
2+
3+
/// Represents errors related to validation failures.
4+
public enum ValidationError: Throwable {
5+
/// The input provided is invalid.
6+
/// - Parameters:
7+
/// - field: The name of the field that caused the error.
8+
case invalidInput(field: String)
9+
10+
/// A required field is missing.
11+
/// - Parameters:
12+
/// - field: The name of the required fields.
13+
case missingField(field: String)
14+
15+
/// The input exceeds the maximum allowed length.
16+
/// - Parameters:
17+
/// - field: The name of the field that caused the error.
18+
/// - maxLength: The maximum allowed length for the field.
19+
case inputTooLong(field: String, maxLength: Int)
20+
21+
/// Generic error message if the existing cases don't provide the required details.
22+
case generic(userFriendlyMessage: String)
23+
24+
/// A user-friendly error message suitable for display to end users.
25+
public var userFriendlyMessage: String {
26+
switch self {
27+
case .invalidInput(let field):
28+
return String(
29+
localized: "BuiltInErrors.ValidationError.invalidInput",
30+
defaultValue: "The value provided for \(field) is invalid. Please correct it.",
31+
bundle: .module
32+
)
33+
case .missingField(let field):
34+
return String(
35+
localized: "BuiltInErrors.ValidationError.missingField",
36+
defaultValue: "\(field) is a required field. Please provide a value.",
37+
bundle: .module
38+
)
39+
case .inputTooLong(let field, let maxLength):
40+
return String(
41+
localized: "BuiltInErrors.ValidationError.inputTooLong",
42+
defaultValue: "\(field) exceeds the maximum allowed length of \(maxLength) characters. Please shorten it.",
43+
bundle: .module
44+
)
45+
case .generic(let userFriendlyMessage):
46+
return userFriendlyMessage
47+
}
48+
}
49+
}

0 commit comments

Comments
 (0)