Saltar al contenido

Manejo de errores y typed throws

Errores explícitos con throws, do/try/catch, y la novedad de Swift 6.3: typed throws, que declara exactamente qué errores puede lanzar una función.

En Swift los errores no se ignoran en silencio: el sistema de tipos te obliga a reconocer que algo puede fallar y a decidir qué hacer. Con Swift 6.3, además, puedes declarar exactamente qué errores lanza una función. Errores como ciudadanos de primera clase.

  • Definir y lanzar errores con throws.
  • Capturarlos con do / try / catch.
  • try? y try! y cuándo usarlos.
  • Typed throws (Swift 6.3) y el tipo Result.

Definir errores

Un error es cualquier tipo que conforme al protocolo Error. Los enums son perfectos para ello:

SWIFT
enum ErrorRed: Error {    case sinConexion    case codigoInvalido(Int)    case datosCorruptos}

Lanzar y propagar: throws

Una función que puede fallar se marca con throws y lanza con throw:

SWIFT
func descargar(_ url: String) throws -> String {    guard url.hasPrefix("https") else {        throw ErrorRed.sinConexion    }    return "datos de \(url)"}

Capturar: do / try / catch

Al llamar a una función que lanza, usas try dentro de un bloque do, y capturas con catch:

SWIFT
do {    let datos = try descargar("http://inseguro.com")    print(datos)} catch ErrorRed.sinConexion {    print("Sin conexión")} catch {    print("Otro error: \(error)")   // 'error' está disponible por defecto}

El catch sin patrón captura cualquier error restante, y te da la variable error automáticamente.

try? y try!

Dos atajos para casos concretos:

SWIFT
// try? convierte el resultado en opcional: nil si lanzalet datos = try? descargar("http://x.com")   // String? → nil si falla// try! fuerza: si lanza, CRASHEA. Solo si es imposible que falle.let seguro = try! descargar("https://ok.com")

try! es como el force unwrap

try! desactiva el manejo de errores y crashea si algo falla. Igual que el ! de los opcionales, resérvalo para lo verdaderamente imposible. En código normal, do/catch o try? son la opción segura.

Typed throws (Swift 6.3)

Novedad clave de Swift 6.3: declarar qué tipo de error lanza una función, no solo que “puede lanzar”:

SWIFT
// Esta función SOLO puede lanzar ErrorRedfunc descargar(_ url: String) throws(ErrorRed) -> String {    guard url.hasPrefix("https") else { throw .sinConexion }    return "datos"}

Por qué importa typed throws

Con throws normal, el catch recibe un any Error y tienes que adivinar o comprobar tipos. Con throws(ErrorRed), el compilador sabe exactamente qué puede fallar: en el catch, error ya es un ErrorRed, y el switch sobre sus casos es exhaustivo. Ganas la misma seguridad que con los enums: imposible olvidar manejar un caso de error. Es especialmente valioso en librerías y en código donde el rendimiento importa (embebido), porque evita la indirección del error genérico.

Result: errores como valor

A veces quieres el éxito o el error como un valor que pasar por ahí (en callbacks, por ejemplo). Para eso está Result:

SWIFT
func cargar() -> Result<String, ErrorRed> {    .success("contenido")    // o .failure(.datosCorruptos)}switch cargar() {case .success(let datos): print(datos)case .failure(let err):   print("Falló: \(err)")}

defer: limpieza garantizada

defer ejecuta código al salir del ámbito, pase lo que pase (éxito o error). Perfecto para liberar recursos:

SWIFT
func procesar() throws {    print("abrir recurso")    defer { print("cerrar recurso") }   // se ejecuta siempre al salir    try descargar("https://x.com")    // aunque esto lance, el 'defer' cierra el recurso}

Falla con elegancia

  1. Define un enum ErrorValidacion: Error con un par de casos.
  2. Escribe una función throws que valide una edad y lance si es negativa.
  3. Llámala en un do/catch y maneja el error.
  4. Reescríbela con throws(ErrorValidacion) (typed throws) y captura con un switch exhaustivo.
  5. Añade un defer que imprima “validación terminada”.