Reflexión e introspección en Swift: mirando dentro del alma del código
\n\n¿Alguna vez te has preguntado cómo Swift «ve» sus propias estructuras y clases? ¿Cómo puedes conocer sobre la marcha el tipo de un objeto, sus propiedades o incluso la firma de un método, sin saber nada de él de antemano? Bienvenido al mundo de la reflexión y la introspección: dos poderosos mecanismos que convierten a Swift, de tipificación estática, en una herramienta flexible para la metaprogramación.
\n\nHoy no solo analizaremos la teoría. Escribiremos código que se investiga a sí mismo, aprenderemos a verificar protocolos dinámicamente e incluso serializar objetos sin Codable. ¡Allá vamos!
\n\n¿Qué son la reflexión y la introspección? No confundas estos conceptos
\n\nEmpecemos con una terminología clara para no perdernos en los recovecos del Swift runtime.
\n\n- \n
- Introspección — es la capacidad de un programa de «mirarse a sí mismo» durante la ejecución: conocer el tipo de un objeto, la cantidad de propiedades, los nombres de los métodos. En Swift esto se implementa a través del protocolo
Mirrory los operadoresis,as?,type(of:). \n - Reflexión — es un concepto más amplio. No solo es observación, sino también la posibilidad de influir en la estructura del programa sobre la marcha: llamar métodos por nombre, cambiar propiedades, crear nuevos tipos. En Swift la reflexión es limitada (a diferencia de Python o Ruby), pero sus capacidades son impresionantes. \n
La principal diferencia: la introspección es lectura, la reflexión es lectura + escritura. Swift se posiciona como un lenguaje seguro, por lo que la reflexión completa (como en Objective-C) no está disponible para tipos puramente Swift. Pero para las clases heredadas de NSObject, se abren más posibilidades.
Herramientas de introspección en Swift: de lo simple a lo complejo
\n\n1. Operadores is y as? — los clásicos del género
\n\nLa forma más sencilla de conocer el tipo de un objeto es usar la verificación con is:
let value: Any = "¡Hola, Swift!"\n\nif value is String {\n print("¡Esto es una cadena!")\n}\n\nif let number = value as? Int {\n print("Esto es un número: \\(number)")\n} else {\n print("Esto no es un número")\n}\n\nAquí usamos is para verificar el tipo y as? para una conversión segura. Esta es la base de cualquier introspección en Swift: constantemente le preguntas al objeto: «¿Quién eres?».
2. type(of:) — conocemos el tipo dinámicamente
\n\nLa función type(of:) devuelve el metatipo (Any.Type) del valor pasado. Esto es útil cuando necesitas obtener el tipo dinámico exacto, no el estático declarado:
class Animal { }\nclass Dog: Animal { }\n\nlet pet: Animal = Dog()\nprint(type(of: pet)) // Dog\n\n// Comparación de tipos\nif type(of: pet) == Dog.self {\n print("¡Esto es definitivamente un perro!")\n}\n\nObserva: type(of:) devuelve el tipo dinámico, no el estático (Animal). Esta es una diferencia clave con respecto al simple Self.
3. Mirror — visión de rayos X para objetos
\n\nY ahora lo más interesante. Mirror es una estructura que permite mirar dentro de cualquier objeto y obtener una lista de sus propiedades, sus nombres y valores. Una verdadera herramienta para la reflexión en Swift.
Ejemplo básico:
\n\nstruct User {\n let name: String\n var age: Int\n private var secretKey: String\n}\n\nlet user = User(name: "Alexey", age: 30, secretKey: "abc123")\nlet mirror = Mirror(reflecting: user)\n\nprint("Tipo del objeto: \\(mirror.subjectType)")\nprint("Cantidad de propiedades: \\(mirror.children.count)")\n\nfor child in mirror.children {\n if let label = child.label {\n print("\\(label): \\(child.value)")\n }\n}\n\nSalida:
\nTipo del objeto: User\nCantidad de propiedades: 3\nname: Alexey\nage: 30\nsecretKey: abc123\n\nObserva: ¡Mirror ve incluso las propiedades privadas! Esto es poderoso, pero también peligroso — no se