Problem
In multiple sections of my code, I need to execute three operations every time I handle an Optional:
- Unwrap the Optional content
- Log an error in the logging system if the value is nil
- Throw an exception when the value doesnβt exist
This pattern repeats frequently, generating duplicate code and reducing maintainability.
Solution
The strategy consists of encapsulating the three operations in reusable functions that work in a coordinated manner.
Helper function: logError
func logError(
_ message: String,
status: HTTPResponseStatus
) throws -> Never {
self.logMessage(message, level: .error)
throw Abort(status, reason: message)
}
This function executes the error logging in the logging system and subsequently terminates execution by throwing an Abort exception. The Never return type is fundamental, as it indicates to the compiler that this function never returns normally, ensuring that execution is completely interrupted.
Main function: guardAndLogError
func guardAndLogError<T>(
_ optional: T?,
message: String,
status: HTTPResponseStatus = .noContent
) throws -> T {
guard let optional else {
try logError(message, status: status)
}
return optional
}
This generic function implements the complete pattern:
- Uses guard let to safely unwrap the Optional
- If the value is nil, invokes logError() to log the failure and terminate execution
- If it contains a value, returns it successfully
The genericity T allows using this function with any type of optional data.
Result
With this implementation, a single line of code executes the three required operations: safe unwrapping, error logging, and exception handling.
let fileName = try guardAndLogError(
fileName,
message: "fileName value not found"
)
Benefits
β
Reduction of duplicate code
β
Consistent error handling
β
Centralized and structured logs
β
Reusability through genericity
Extensibility
This pattern can be extended for more specific use cases:
// For boolean validations
func guardAndLogError(
_ condition: Bool,
message: String
) throws { ... }
// For arrays
func guardAndLogError<T>(
_ optionals: T?...,
message: String
) throws -> [T] { ... }
// For tuples
func guardAndLogError<T, U>(
_ first: T?,
_ second: U?
, message: String
) throws -> (T, U) { ... }
Keep coding, keep running πββοΈ