I need to get the properties of my class as a dictionary. For simplicity, I created a protocol which has a default implementation as follows:
protocol ListsProperties{
func toDictionary() -> [String: AnyObject]
}
extension ListsProperties{
func toDictionary() -> [String: AnyObject] {
let mirrored_object = Mirror(reflecting: self)
var dict = [String: AnyObject]()
for (_, attr) in mirrored_object.children.enumerate() {
if let propertyName = attr.label as String! {
dict[propertyName] = attr.value as? AnyObject
}
}
return dict
}
}
My classes can conform to this protocol and will have the toDictionary() method available. However, this does not work if I use the method on a subclass, as it will produce only the properties defined on the subclass and ignore the parent superclass properties.
Ideally I could find some way to call the toDictionary() method on the mirrored superclass as this would then call toDictionary() on its own superclass and the compiler says that the superclass mirror does not conform to the Protocol even though the class it is mirroring does.
The following works but only if there is only one superclass so isn't sufficient:
func toDictionary() -> [String: AnyObject] {
let mirrored_object = Mirror(reflecting: self)
var dict = [String: AnyObject]()
for (_, attr) in mirrored_object.children.enumerate() {
if let propertyName = attr.label as String! {
dict[propertyName] = attr.value as? AnyObject
}
}
// This is an issue as it limits to one subclass 'deep'
if let parent = mirrored_object.superclassMirror(){
for (_, attr) in parent.children.enumerate() {
if let propertyName = attr.label as String!{
if dict[propertyName] == nil{
dict[propertyName] = attr.value as? AnyObject
}
}
}
}
return dict
}
Any ideas on how I could modify the default implementation of toDictionary() to include superclass attributes (and the attributes of any superclasses of the superclass etc)?
My solution is to use
Instead of
properties is a DictionnaryLiteral object containing the properties I want to be mirrored.
Heres a full version of toDictionary above.
It uses the method where toDictionary() is an extension on Mirror
I added recursion so it can handle any depth of class hierarchy.
It get mirror.children on self >> self.super >> self.super.super .. etc
I plan to use this myself to convert my Model objects to CKRecord.
Helper Method
The "helper method" above would work too but as that just uses methods in Mirror adding all the code as an extension to Mirror makes logical sense too.
Just paste all the code below in a Playground and should work.
Execution start at bottom block of code see:
//EXECUTION STARTS HERE
OUTPUT
One possible solution would be to implement
toDictionary()
as a method ofMirror
itself, so that you can traverse recursively to the superclass mirror:and then use that to implement the protocol extension method:
Here is my Implementation
.
Usage
By using a protocol, you require that all the superclasses implement such a protocol to be able to use the function.
I would use a helper class, so that you can pass any object to it.
Then you can use it like
If you still want to be able to write
you can implement the protocol with the extension calling the helper class.
You can simply loop through all the
superclassMirror
s get the properties. It doesn't matter how many layers there are.var mirror: Mirror? = Mirror(reflecting: child) repeat { for property in mirror!.children { print("property: \(property)") } mirror = mirror?.superclassMirror() } while mirror != nil