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 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”:
// 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:
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}Falla con elegancia
- 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”.