El pattern matching (coincidencia de patrones) es una característica fundamental de Swift que hace que el código sea más seguro y limpio. Históricamente, el pattern matching ha evolucionado como un medio para trabajar de manera más expresiva y segura con enums, especialmente con associated values. A diferencia de otros lenguajes, Swift permite desempaquetar valores anidados directamente en las expresiones if, guard y switch.
Problema: para enums no estándar, especialmente con associated values anidados, es fácil cometer errores en la estructura de case o subestimar las limitaciones del switch principal. También a menudo no es obvia la diferencia entre usar if case, guard case y el switch clásico.
Solución: aplicar diferentes enfoques al pattern matching dependiendo del escenario, y en estructuras complejas anidadas, especificar claramente los nombres y usar where.
Ejemplo de código:
enum NetworkState { case success(User) case failure(Error, code: Int) case loading(progress: Double) } let state = NetworkState.failure(SomeError(), code: 401) if case let .failure(error, code) = state, code == 401 { print("No autorizado: \(error)") } guard case .success(let user) = state else { return } print(user) switch state { case .success(let user): print("Bienvenido, \(user.name)") case .failure(let error, let code) where code == 404: print("No encontrado: \(error)") case .failure(_, let code): print("Fallo con código: \(code)") case .loading(let progress): print("Progreso: \(progress)") }
Características clave:
¿Qué sucede si no se especifican todos los cases de un enum en un switch?
Error de compilación (si el enum es no opcional) o advertencia/requisito de case por defecto, si no se tienen en cuenta todas las coberturas. Para Optionals, es suficiente con especificar solo .some y .none.
¿Se puede hacer pattern matching con enums anidados y associated values?
Sí. Pero la sintaxis se complica. Para los valores anidados, se pueden desempaquetar varios niveles directamente:
enum Outer { case inner(Inner) } enum Inner { case item(id: Int) } let e = Outer.inner(.item(id: 5)) if case let .inner(.item(id)) = e { print(id) }
¿Cuál es la diferencia entre guard case e if case al hacer coincidencia de patrones?
guard case verifica la condición y ejecuta un bloque de salida (return, throw, break) al fallar. Se utiliza generalmente para requisitos: si no coincide, salir.
if case es una construcción que se adapta a manejadores condicionales de una línea sin salir de la función.
Un desarrollador utiliza switch para un enum con varios cases, pero no cubre un nuevo case. El código se compila, pero el nuevo escenario no se maneja y la aplicación se bloquea en tiempo de ejecución.
Pros:
Contras:
Un desarrollador cubre explícitamente todos los cases, usa where para filtros locales, desempaqueta valores anidados a través de let anidado.
Pros:
Contras: