Guard and LogError

Jorge corriendo mientras abre una bebida de la que salen confetis sin esperarlo. En el bocadillo pone GUARD and LOGERROR.

Tabla de contenidos


Problema

En múltiples secciones de mi código necesito ejecutar tres operaciones cada vez que manejo un Optional:

  1. Desempaquetar el contenido del Optional
  2. Registrar un error en el sistema de logs si el valor es nil
  3. 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 🏃‍♂️