Manejo de Errores
## Objetivos de Aprendizaje
- Comprender el manejo de errores de Swift
- Lanzar y capturar errores
- Usar tipo Result
- Manejar errores apropiadamente
## Protocolo Error
### Definir Errores
```swift
enum ErrorValidacion: Error {
case vacio
case muyCorto
case caracterInvalido
}
```
### Tipos de Error Personalizados
```swift
enum ErrorRed: Error {
case urlInvalida
case sinConexion
case tiempoAgotado
case respuestaInvalida
case errorServidor(Int)
}
```
## Lanzar Errores
### Usando throw
```swift
func validar(nombre: String?) throws {
guard let nombre = nombre else {
throw ErrorValidacion.vacio
}
guard nombre.count >= 2 else {
throw ErrorValidacion.muyCorto
}
guard nombre.allSatisfy({ $0.isLetter }) else {
throw ErrorValidacion.caracterInvalido
}
}
```
### Funciones que Lanzan
```swift
func puedeLanzar() throws -> String {
// Funcion que puede lanzar
throw AlgundoError.ups
return "Exito"
}
// vs
func noPuedeLanzar() -> String {
// Funcion que no puede lanzar
return "Exito"
}
```
## Expresion Try
### try Basico
```swift
do {
let resultado = try validar(nombre: "Alicia")
print("Valido: \(resultado)")
} catch {
print("Ocurrio un error")
}
```
### try? (Try Opcional)
Devuelve nil en error:
```swift
let resultado = try? validar(nombre: "A")
// resultado es nil si se lanzo un error
if let valido = try? validar(nombre: "Alicia") {
print(valido)
}
```
### try! (Try Forzado)
Crashea en error:
```swift
let resultado = try! validar(nombre: "Alicia")
// CRASHEA si se lanzo un error!
```
## Clausulas Catch
### Catch Basico
```swift
do {
try validar(nombre: "")
} catch {
print("Validacion fallida")
}
```
### Capturar Errores Especificos
```swift
do {
try validar(nombre: "")
} catch ErrorValidacion.vacio {
print("El nombre no puede estar vacio")
} catch ErrorValidacion.muyCorto {
print("El nombre es muy corto")
} catch ErrorValidacion.caracterInvalido {
print("El nombre contiene caracteres invalidos")
}
```
### Capturar con Patron
```swift
do {
try obtenerDatos(de: "invalido")
} catch ErrorRed.errorServidor(let codigo) {
print("Error de servidor con codigo: \(codigo)")
} catch ErrorRed.tiempoAgotado {
print("La solicitud expire")
} catch {
print("Error desconocido: \(error)")
}
```
### Capturar Cualquier Error
```swift
do {
try operacionRisky()
} catch let error as MiError {
print("Capturado error especifico: \(error)")
} catch {
print("Capturado cualquier error: \(error)")
}
```
## Propagacion de Errores
### Propagar Errores
```swift
func procesarEntradaUsuario(_ entrada: String?) throws {
guard let nombre = entrada else {
throw ErrorValidacion.vacio
}
try validar(nombre: nombre) // Errores se propagan
}
do {
try procesarEntradaUsuario(nil)
} catch {
print("Manejado en nivel superior: \(error)")
}
```
### Rethrowing
```swift
func manejarErrores(_ operacion: () throws -> Void) rethrows {
do {
try operacion()
} catch {
print("Manejado: \(error)")
}
}
```
## Tipo Result
### Enum Result
```swift
enum Result {
case exito(Exito)
case fracaso(Fracaso)
}
```
### Usar Result
```swift
func obtenerUsuario(id: Int) -> Result {
if id > 0 {
return .exito(Usuario(id: id, nombre: "Alicia"))
} else {
return .fracaso(ErrorRed.respuestaInvalida)
}
}
```
### Manejar Result
```swift
let resultado = obtenerUsuario(id: 1)
switch resultado {
case .exito(let usuario):
print("Obtuve usuario: \(usuario.nombre)")
case .fracaso(let error):
print("Error: \(error)")
}
```
### Result con Opcional
```swift
let resultado = Result.exito(Usuario(id: 1, nombre: "Alicia"))
// Never significa que no puede fallar
```
### Metodo get()
```swift
let exito = Result.exito(42)
let fracaso = Result.fracaso(ErrorRed.tiempoAgotado)
try? exito.get() // Optional(42)
try? fracaso.get() // nil
```
## Ejecucion Diferida
### Declaracion defer
```swift
func leerArchivo() {
let archivo = abrirArchivo("datos.txt")
defer {
cerrarArchivo(archivo) // Siempre ejecutado antes del return
}
// Trabajar con archivo
let contenido = leerContenido(archivo)
// Archivo cerrado automaticamente aqui
}
```
### Multiples defers
```swift
func ejemplo() {
defer { print("1") }
defer { print("2") }
defer { print("3") }
}
// Salida: 3, 2, 1 (orden LIFO)
```
### defer con Manejo de Errores
```swift
func procesar() throws {
let recurso = adquirirRecurso()
defer {
liberarRecurso(recurso) // Siempre liberado
}
try hacerTrabajo(recurso)
// recurso liberado si hay exito o fracaso
}
```
## Manejo de Errores Personalizado
### Conformidad a Error
```swift
struct ErrorPersonalizado: Error {
let mensaje: String
}
throw ErrorPersonalizado(mensaje: "Algo salio mal")
```
### LocalizedError
```swift
struct ErrorArchivoNoEncontrado: Error, LocalizedError {
let nombreArchivo: String
var errorDescription: String? {
return "Archivo no encontrado: \(nombreArchivo)"
}
}
```
### Custom NSError
```swift
extension ErrorPersonalizado: CustomNSError {
var errorCode: Int {
return 42
}
var errorUserInfo: [String: Any] {
return ["clavePersonalizada": "valorPersonalizado"]
}
}
```
## Mejores Practicas
### Hacer
```swift
// Usar tipos de error especificos
enum ErrorApp: Error {
case entradaInvalida
case falloRed
case noEncontrado
}
// Propagar errores cuando sea apropiado
func cargarDatos() throws -> Data {
let url = try obtenerURL()
return try obtener(de: url)
}
// Usar Result para callbacks
completacion(.exito(datos))
completacion(.fracaso(error))
```
### No Hacer
```swift
// No tragar errores silenciosamente
do {
try operacion()
} catch {
// MAL: No hacer nada oculta bugs!
}
// No usar try! sin justificacion
let valor = try! parse(json) // Solo si estas seguro
```
## Patrones Comunes
### Manejo de Errores con Opcionales
```swift
// Devuelve nil en lugar de lanzar
func parsearSeguro(_ cadena: String) -> Int? {
return Int(cadena)
}
let numero = parsearSeguro("42") ?? 0
```
### Encadenamiento Opcional Fallido
```swift
struct Usuario {
let nombre: String
}
func obtenerUsuario() throws -> Usuario { ... }
let nombre = try? obtenerUsuario()?.nombre
// nombre es nil si obtenerUsuario() devuelve nil o lanza
```
### Transformar Errores
```swift
enum ErrorApp: Error {
case emailInvalido
}
func validar(email: String) throws {
guard email.contains("@") else {
throw ErrorApp.emailInvalido
}
}
let email: String? = "test@ejemplo.com"
try? email.flatMap { try? validar(email: $0) }
// Aplana opcional y manejo de errores
```
## Resumen
- Los errores conforman el protocolo `Error`
- Funciones que lanzan marcadas con `throws`
- Usar `do-catch` para manejar errores
- `try?` devuelve nil en error
- `try!` crashea en error (usar con moderacion)
- `Result` para operaciones que devuelven errores
- `defer` para codigo de limpieza
- Propagar errores con `rethrows`
- Siempre manejar errores apropiadamente
Comments
Comments powered by Giscus
To enable comments, add your Giscus embed code here.
Learn more about Giscus →