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)?
One possible solution would be to implement toDictionary()
as a method of Mirror
itself, so that you can traverse recursively
to the superclass mirror:
extension Mirror {
func toDictionary() -> [String: AnyObject] {
var dict = [String: AnyObject]()
// Properties of this instance:
for attr in self.children {
if let propertyName = attr.label {
dict[propertyName] = attr.value as? AnyObject
}
}
// Add properties of superclass:
if let parent = self.superclassMirror() {
for (propertyName, value) in parent.toDictionary() {
dict[propertyName] = value
}
}
return dict
}
}
and then use that to implement the protocol extension method:
extension ListsProperties {
func toDictionary() -> [String: AnyObject] {
return Mirror(reflecting: self).toDictionary()
}
}
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
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.
class ListsPropertiesHelper {
static func toDictionary(mirrored_object: Mirror) -> [String: AnyObject] {
var dict = [String: AnyObject]()
for (_, attr) in mirrored_object.children.enumerate() {
if let propertyName = attr.label as String! {
dict[propertyName] = attr.value as? AnyObject
}
}
if let parent = mirrored_object.superclassMirror() {
let dict2 = toDictionary(parent)
for (key,value) in dict2 {
dict.updateValue(value, forKey:key)
}
}
return dict
}
static func toDictionary(obj: AnyObject) -> [String: AnyObject] {
let mirrored_object = Mirror(reflecting: obj)
return self.toDictionary(mirrored_object)
}
}
Then you can use it like
let props = ListsPropertiesHelper.toDictionary(someObject)
If you still want to be able to write
let props = someObject.toDictionary()
you can implement the protocol with the extension calling the helper class.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With