@dynamicMemberLookup est une annotation Swift permettant de remplacer l'accès aux propriétés d'un objet à l'exécution via subscript. Historiquement, ce mécanisme a été introduit pour une intégration plus transparente avec des langages dynamiques tels que Python, et vise également à une redirection plus flexible de l'accès aux données dans des objets proxy, des modèles dynamiques et des DSL.
Problème : Swift standard exige que les propriétés soient déclarées explicitement et vérifie leur existence au moment de la compilation. Mais il est parfois nécessaire d'accéder aux propriétés par leur nom, qui n'existe pas au moment de la compilation, par exemple lors du travail avec JSON, des API, des objets proxy, ou des wrappers de scripts.
Solution : Utiliser @dynamicMemberLookup et implémenter subscript(dynamicMember:) pour intercepter les tentatives d'accès à des propriétés non existantes.
Exemple de code :
@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
Caractéristiques clés :
Peut-on mettre en œuvre dynamicMemberLookup pour des classes, ou seulement pour des structures ?
Oui, cela peut être appliqué aux classes, structures et énumérations (à partir de Swift 5+). Il est seulement important d'implémenter le subscript correspondant.
Que se passe-t-il si l'on tente d'accéder à une propriété non existante via dynamicMemberLookup ?
La valeur est renvoyée par le subscript, la responsabilité de la gestion de l'absence de valeur repose sur votre implémentation.
Par exemple, dans l'exemple ci-dessus, si l'on accède à user.secret, nil sera renvoyé.
Peut-on effectuer un accès dynamique avec des clés d'un autre type, par exemple Int ?
Oui ! On peut déclarer subscript(dynamicMember:) avec d'autres labels d'argument et le combiner avec des subscripts ordinaires.
@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] } }
Un développeur utilise @dynamicMemberLookup pour un modèle d'utilisateur ordinaire (User), afin d'accéder à n'importe quel champ via des chaînes. Le code devient opaque, l'IDE a perdu les suggestions.
Avantages :
Inconvénients :
@dynamicMemberLookup est appliqué pour travailler avec un objet JSON arbitraire, dont les champs sont inconnus à l'avance. Le mécanisme permet de travailler de manière élégante et sécurisée (via Any?/binding optionnel) avec des données non structurées.
Avantages :
Inconvénients :