@dynamicMemberLookup es una anotación de Swift que permite sobreescribir el acceso a las propiedades de un objeto en tiempo de ejecución a través de subscript. Históricamente, este mecanismo se introdujo para una integración más transparente con lenguajes dinámicos como Python, y también tiene como objetivo un enrutamiento más flexible del acceso a datos en objetos proxy, modelos dinámicos y DSL.
Problema: Swift estándar requiere declarar propiedades de forma explícita y verifica su existencia en tiempo de compilación. Pero a veces es necesario acceder a propiedades por su nombre, que no existe en tiempo de compilación, por ejemplo, al trabajar con JSON, API, objetos proxy, envoltorios de scripts.
Solución: Utilizar @dynamicMemberLookup e implementar subscript(dynamicMember:) para interceptar intentos de acceso a propiedades que no existen.
Ejemplo de código:
@dynamicMemberLookup struct JSON { private var data: [String: Any] subscript(dynamicMember member: String) -> Any? { data[member] } } let user = JSON(data: ["name": "Anna", "age": 23]) print(user.name as? String) // Anna
Características clave:
¿Se puede implementar dynamicMemberLookup para clases, o solo para structs?
Sí, se puede aplicar a clases, structs y enums (a partir de Swift 5+). Solo es importante implementar el subscript correspondiente.
¿Qué pasa si se accede a una propiedad inexistente a través de dynamicMemberLookup?
Se devuelve un valor del subscript, la responsabilidad de manejar la ausencia de un valor recae en tu implementación.
Por ejemplo, en el ejemplo anterior, si se accede a user.secret, se devolverá nil.
¿Se puede hacer acceso dinámico utilizando claves de otro tipo, por ejemplo, Int?
¡Sí! Se puede declarar subscript(dynamicMember:) con otras etiquetas de argumento y combinar con subscripts normales.
@dynamicMemberLookup struct ArrayProxy { private let array: [Int] subscript(dynamicMember member: String) -> Int? { if member == "first" { return array.first } if member == "last" { return array.last } return nil } subscript(index: Int) -> Int? { array[safe: index] } }
El desarrollador utiliza @dynamicMemberLookup para un modelo de usuario normal (User) para acceder a cualquier campo a través de cadenas. El código se vuelve opaco, la IDE perdió sugerencias.
Pros:
Contras:
@dynamicMemberLookup se aplica para trabajar con un objeto JSON arbitrario, cuyos campos son desconocidos de antemano. El mecanismo permite trabajar de manera elegante y segura (a través de Any?/binding opcional) con datos no estructurados.
Pros:
Contras: