Problema
En múltiples secciones de mi código necesito ejecutar tres operaciones cada vez que manejo un Optional:
- Desempaquetar el contenido del Optional
- Registrar un error en el sistema de logs si el valor es nil
- Lanzar una excepción cuando el valor no existe
Este patrón se repite frecuentemente, generando código duplicado y reduciendo la mantenibilidad.
Solución
La estrategia consiste en encapsular las tres operaciones en funciones reutilizables que trabajen de forma coordinada.
Función auxiliar: logError
func logError(
_ message: String,
status: HTTPResponseStatus
) throws -> Never {
self.logMessage(message, level: .error)
throw Abort(status, reason: message)
}
Esta función ejecuta el registro del error en el sistema de logs y posteriormente termina la ejecución lanzando una excepción Abort. El tipo de retorno Never es fundamental, ya que indica al compilador que esta función nunca retorna normalmente, garantizando que la ejecución se interrumpa completamente.
Función principal: guardAndLogError
func guardAndLogError<T>(
_ optional: T?,
message: String,
status: HTTPResponseStatus = .noContent
) throws -> T {
guard let optional else {
try logError(message, status: status)
}
return optional
}
Esta función genérica implementa el patrón completo:
- Utiliza guard let para desempaquetar el Optional de forma segura
- Si el valor es nil, invoca logError() para registrar el fallo y terminar la ejecución
- Si contiene un valor, lo retorna exitosamente
La genericidad T permite usar esta función con cualquier tipo de dato opcional.
Resultado
Con esta implementación, una sola línea de código ejecuta las tres operaciones requeridas: desempaquetado seguro, logging de errores y manejo de excepciones.
let fileName = try guardAndLogError(
fileName,
message: "Valor fileName no encontrado"
)
Beneficios
✅ Reducción de código duplicado
✅ Manejo consistente de errores
✅ Logs centralizados y estructurados
✅ Reutilización mediante genericidad
Extensibilidad
Este patrón puede extenderse para casos de uso más específicos:
// Para validaciones booleanas
func guardAndLogError(
_ condition: Bool,
message: String
) throws { ... }
// Para arrays
func guardAndLogError<T>(
_ optionals: T?...,
message: String
) throws -> [T] { ... }
// Para tuplas
func guardAndLogError<T, U>(
_ first: T?,
_ second: U?
, message: String
) throws -> (T, U) { ... }
Keep coding, keep running 🏃♂️