Caution
This project is experimental and not sufficiently tested.
Leverage guard to make Swift error handling similar to Go, but with extra safety. Introduces the !> and !>> operators.
Note
The primary purpose of this project was to try and see how far I could push the syntactic sugar of Swift to more closely mimic the error handling of Go.
import Err
func processUser(id: String) -> Result<User, Error> {
var err: Error = .nil // Required: initialize an error (Error.nil is provided for convenience)
guard let data = try fetchUserData(id) !> err else {
// 'err' is guaranteed to be any thrown error from the try
// because of 'guard' we MUST return from this function
return .failure(err)
}
return .success(user)
}
func processUserAsync(id: String) async -> Result<User, Error> {
var err: Error = .nil
// for async calls, use the '!>>' operator
// unfortunately, the double await and extra parens are also required most of the time
guard let data = await (try await fetchUserData(id)) !>> err else {
return .failure(err)
}
return .success(user)
}Add to your Package.swift:
dependencies: [
.package(url: "https://github.com/walteh/swift-err.git", branch: "main")
]The operators leverage Swift's guard statements for type safety:
func processData() -> Result<String, Error> {
var err: Error = .nil
guard let result = try parseData() !> err else {
// Compiler guarantees we MUST handle the error case
// After this point, execution cannot continue
return .failure(err)
}
// Compiler guarantees 'result' is valid here
// The guard statement provides definitive type safety
return .success(result)
}The guard pattern ensures:
- Definitive error handling: You must handle the error case
- Type certainty: After the guard, your value is guaranteed valid
- Early returns: Failed operations exit immediately
Optionally wrap errors with caller info and a message using Error.wrap:
throw err.wrap("loading user from database")Also supported by the operators using .wrap(&err, "") syntax:
guard let result = try parseData() !> .wrap(&err, "Failed parsing") else {
// err contains original error + your context message
print(err)
// to get the actual error, you can call the cause() function
throw err.cause()
}By default, wrapped errors include caller info (file name, line, function) plus your message.
To disable caller info globally:
disableCallerInfo()Licensed under the Apache License 2.0.