Reputation: 2593
I have several swift classes that look similar like the following
public class Book {
var title: String?
var date: NSDate?
}
As there are several different classes where I need to access the properties, I am using reflection to run through the properties of the class:
let myBook = Book()
myBook.title = "Hello"
myBook.date = NSDate()
let mirror = Mirror(reflecting: myBook)
var propsArr = [(key: String?, value: Any)]()
let mirrorChildrenCollection = AnyRandomAccessCollection(mirror.children)!
if mirrorChildrenCollection.count > 0 {
propsArr += mirrorChildrenCollection
}
//iterate through properties
for case let (label?, value) in propsArr {
print (label, value)
if let val = value as? NSDate {
var extractedDate = val
print(extractedDate)
}
else if let val = value as? String {
var extractedTitle = val
print (extractedTitle)
}
}
But I have a problem that the Child objects are not extracted as they are of Type Any and internally optional classes and thus do not fall into my cases. If I change title from String? to String, they do work, but I need to use optional types.
What can I change in the above implementation to leave the datatype as String? and Date? and still extract the values from the Mirror?
Upvotes: 8
Views: 2934
Reputation: 135558
It seems this isn't possible in Swift 2.x.
Since the properties are optionals, you would have to cast to NSDate?
and String?
, respectively, like this:
if let val = value as? NSDate? {
// val is Optional<NSDate>
}
Unfortunately, the compiler doesn't allow this (I’m not sure why): // error: cannot downcast from 'protocol<>' to a more optional type 'NSDate?'
.
This answer from bubuxu provides a clever workaround that would work if you had a Mirror
for each property. The mirror's displayStyle
property tells you if it is an optional, and you can then extract the wrapped value manually. So this would work:
let child = Mirror(reflecting: myBook.date)
child.displayStyle
if child.displayStyle == .Optional {
if child.children.count == 0 {
// .None
} else {
// .Some
let (_, some) = child.children.first!
if let val = some as? NSDate {
print(val)
}
}
}
But this depends on having a Mirror
for each property, and it seems you can’t traverse a Mirror
's children to retrieve Mirror
s for them.
Upvotes: 10