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?ytry!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:
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:
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:
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:
// try? convierte el resultado en opcional: nil si lanza
let 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! 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”:
// Esta función SOLO puede lanzar ErrorRed
func descargar(_ url: String) throws(ErrorRed) -> String {
guard url.hasPrefix("https") else { throw .sinConexion }
return "datos"
}
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:
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:
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
}
- Define un enum
ErrorValidacion: Errorcon un par de casos. - Escribe una función
throwsque valide una edad y lance si es negativa. - Llámala en un
do/catchy maneja el error. - Reescríbela con
throws(ErrorValidacion)(typed throws) y captura con unswitchexhaustivo. - Añade un
deferque imprima “validación terminada”.