Manejo de errores en Swift: try/catch y más — nivel avanzado
El manejo de errores es una parte esencial de cualquier desarrollo serio de iOS/macOS. Swift proporciona un sistema de gestión de errores potente y expresivo que va mucho más allá del simple try/catch. En este artículo, exploraremos técnicas avanzadas: trabajo con el protocolo Error, enumeraciones de errores, las palabras clave throws, rethrows, así como el tipo Result y enfoques funcionales. El material está dirigido a desarrolladores que ya conocen los fundamentos de Swift.
1. Protocolo Error y tipos de error personalizados
En la base del manejo de errores en Swift se encuentra el protocolo Error. Cualquier tipo que cumpla con este protocolo puede ser lanzado como un error. La mayoría de las veces se utilizan enumeraciones (enum) para esto.
import Foundation
// Definimos un tipo de error personalizado para una solicitud de redenum NetworkError: Error { case invalidURL case noData case decodingFailed(description: String) case serverError(statusCode: Int)}
// Función que puede lanzar un errorfunc fetchUserData(from urlString: String) throws -> Data { guard let url = URL(string: urlString) else { throw NetworkError.invalidURL }
// Simulación de una solicitud de red let success = Bool.random() if !success { throw NetworkError.serverError(statusCode: 500) }
return Data()}El uso de enumeraciones permite agrupar errores relacionados y transmitir información adicional (associated values), lo que hace que el código sea más legible y autodocumentado.
2. Uso avanzado de do-catch
El bloque do-catch es la herramienta principal para interceptar errores. A nivel avanzado, es importante saber cómo manejar diferentes tipos de errores de manera distinta.
func processUserData() { do { let data = try fetchUserData(from: "https://api.example.com/user") // Trabajamos con los datos print("Datos recibidos: \\(data.count) bytes") } catch NetworkError.invalidURL { print("Error: URL inválida") } catch NetworkError.serverError(let code) where code >= 500 { print("Error del servidor con código \\(code). Intente de nuevo más tarde.") } catch let error as NetworkError { print("Error de red: \\(error)") } catch { // Manejo de todos los demás errores print("Error desconocido: \\(error.localizedDescription)") }}Puntos clave:
- Coincidencia de patrones con
where: permite filtrar errores según condiciones adicionales (por ejemplo, un rango de códigos de respuesta). - Conversión de tipo (
as): si esperas varios tipos de errores, puedes usarcatch let error as MyErrorType. catchuniversal: siempre debe ser el último, ya que captura cualquier error no manejado.
3. Palabras clave throws, rethrows y propagación de errores
La palabra clave throws indica que una función puede lanzar un error. rethrows es un mecanismo más sutil: indica que una función puede lanzar un error solo si uno de sus parámetros de clausura (closure) lanzó un error.
// Función con throws — puede lanzar un error por sí mismafunc loadConfig() throws -> [String: String] { // ... throw NetworkError.noData}
// Función con rethrows — transmite el error desde la clausurafunc performWithRetry(times: Int, operation: () throws -> Void) rethrows { for _ in 0..<times { try operation() }}
// Usodo { try performWithRetry(times: 3) { try loadConfig() }} catch { print("La operación falló después de 3 intentos: \\(erro