Manejo de errores en Swift: try/catch y técnicas avanzadas

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

Manejo de errores en Swift: try/catch y más (nivel avanzado)

\n\n

El manejo de errores es uno de los temas clave en el desarrollo con Swift. Si ya estás familiarizado con lo básico de do-catch y try?, es hora de pasar al siguiente nivel. En este artículo, exploraremos técnicas avanzadas: trabajar con el protocolo Error, el tipo Result, la palabra clave rethrows, y cómo diseñar correctamente una jerarquía de errores en proyectos reales de iOS/macOS.

\n\n

Inmersión profunda en el protocolo Error y errores personalizados

\n\n

En Swift, cualquier error debe cumplir con el protocolo Error. Sin embargo, para hacer el código más legible y mantenible, vale la pena crear enumeraciones (enum) propias con casos anidados y valores asociados.

\n\n
enum NetworkError: Error {\n    case invalidURL\n    case noData\n    case decodingFailed(description: String)\n    case serverError(statusCode: Int)\n    case timeout(seconds: Double)\n}\n\n// Ejemplo de uso\nfunc fetchUser(id: Int) throws -> User {\n    guard id > 0 else {\n        throw NetworkError.invalidURL\n    }\n    // ... lógica de la solicitud\n    if someCondition {\n        throw NetworkError.serverError(statusCode: 500)\n    }\n    return User()\n}
\n\n

Este enfoque permite transmitir contexto adicional: código de error, descripción o tiempo de espera. Esto es especialmente útil para el registro y la depuración.

\n\n

Result: alternativa a do-catch para código asíncrono

\n\n

El tipo Result<Success, Failure> es una herramienta poderosa que proviene de la programación funcional. Es ideal para APIs basadas en callbacks, solicitudes de red y operaciones asíncronas.

\n\n
enum DataError: Error {\n    case corrupted\n    case notFound\n}\n\nfunc loadImage(from url: URL, completion: @escaping (Result<UIImage, DataError>) -> Void) {\n    DispatchQueue.global().async {\n        // Simulación de carga\n        if arc4random_uniform(2) == 0 {\n            completion(.success(UIImage()))\n        } else {\n            completion(.failure(.notFound))\n        }\n    }\n}\n\n// Uso\nloadImage(from: someURL) { result in\n    switch result {\n    case .success(let image):\n        print("Imagen cargada: \\(image)")\n    case .failure(let error):\n        print("Error: \\(error.localizedDescription)")\n    }\n}
\n\n

Result hace que el código sea más predecible y elimina la necesidad de manejar valores opcionales con nil. Además, puedes usar .map y .flatMap para cadenas de transformaciones.

\n\n

Rethrows: cuando una función «propaga» errores

\n\n

La palabra clave rethrows es una de las características más infravaloradas de Swift. Permite escribir una función que no lanza errores por sí misma, pero los «propaga» si el closure pasado es throws.

\n\n
func performOperation<T>(on array: [T], operation: (T) throws -> Bool) rethrows -> [T] {\n    var result: [T] = []\n    for element in array {\n        if try operation(element) {\n            result.append(element)\n        }\n    }\n    return result\n}\n\n// Sin errores (no necesita try)\nlet numbers = [1, 2, 3, 4, 5]\nlet evenNumbers = performOperation(on: numbers) { $0.isMultiple(of: 2) }\nprint(evenNumbers) // [2, 4]\n\n// Con error (necesita try)\nenum ValidationError: Error { case negative }\nlet filtered = try performOperation(on: numbers) { value in\n    guard value > 0 else { throw ValidationError.negative }\n    return value % 2 == 0\n}
\n\n

rethrows hace que la API sea flexible: el usuario puede pasar tanto un closure normal (sin try) como un closure con throws (entonces se requerirá try). Esto se usa a menudo en la biblioteca estándar de Swift, por ejemplo, en map, filter y for.

Blogs

Book Recommendations