Mirror Not Working in Swift When Iterating Through Children of an Objective-C Object

Mirror not working in Swift when iterating through children of an Objective-C object

Mirror does not seem to work with Objective-C classes. But, you can use the class_copyPropertyList function to retrieve all the properties from an Objective-C class.

var outCount : UInt32 = 0
let properties = class_copyPropertyList(ObjectCTest.self, &outCount)

print(outCount)

for i : UInt32 in 0..<outCount
{
let strKey : NSString? = NSString(CString: property_getName(properties[Int(i)]), encoding: NSUTF8StringEncoding)

let attrs : NSString? = NSString(CString: property_getAttributes(properties[Int(i)]), encoding: NSUTF8StringEncoding)

print(strKey)
print(attrs)
}

get Child of Mirror.Children by index

The type Children is actually a typedef for AnyForwardCollection<Child>.

You can't index into an AnyForwardCollection with any old Int. You need to use an AnyForwardIndex. You can use startIndex to get the starting index and advancedBy() to move forward.

let obj = demo()
let reflected = Mirror( reflecting: obj)

let idx = reflected.children.startIndex
let first = reflected.children[idx]
let thirdIdx = idx.advancedBy(2)
let third = reflected.children[thirdIdx]

Mirroring Not Picking Up Base Class on Swift 3

You need to use the superclassMirror: Mirror? property for that. For instance:

for attr in widgetMirror.superclassMirror!.children {
print(attr.label!)
}

prints the expected results:

name

uuid


Update. If you keep running into this, add this Mirror extension to your toolkit:

extension Mirror {
var allChildren: [Mirror.Child] {
var allChildren: [Mirror.Child] = []
var mirror: Mirror! = self
repeat {
allChildren.append(contentsOf: mirror.children)
mirror = mirror.superclassMirror
} while mirror != nil
return allChildren
}
}

Usage:

for attr in widgetMirror.allChildren {
print(attr.label!)
}

Swift 3 Reflection - Iterating over properties

According to documentation about Child here :

Child

An element of the reflected instance’s structure. The optional label may be used when appropriate, e.g. to represent the name of a
stored property or of an active enum case, and will be used for lookup
when Strings are passed to the descendant method.

Since type child in mirror is defined as

typealias Child = (label: String?, value: Any)

Therefore all names are printed as Optionals.

To get the label names you can use optional binding

I have made some changes and tried this in playground:

class FooModel
{
func toString() -> String
{
var result = "[\(String(describing: type(of: self))) "
let mirror = Mirror(reflecting: self)
mirror.children.forEach
{
child in
print(child)
if let label = child.label {
result += "\(label): \(child.value), "
}
}
return "\(result)]"
}
}

class FooProject : FooModel
{
var id = 0
var name = ""
var announcement:String!
var showAnnouncement = false
var isCompleted = false
var completedOn:String!
var suiteMode = 0
var url = ""

init(id: Int, name: String, completedOn: String) {
self.completedOn = completedOn
self.id = id
self.name = name
//...
}
}

let fooProj = FooProject(id: 10, name: "Name", completedOn: "Sunday")
print(fooProj.toString())

Ouptut is :

[FooProject id: 10, name: Name, announcement: nil, showAnnouncement: false, isCompleted: false, completedOn: Sunday, suiteMode: 0, url: , ]

HTH..

How to extract optional typed values from Mirror children in Swift?

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 Mirrors for them.

Swift 2.0 Get Mirrored Superclass Properties

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()
}
}

How to get class properties label in swift 3?

You can use Mirror in swift3 to get all the properties of a class like this

class BasicD{
public var id_: Int64 = 0
public var a: Int32 = 0
public var c: NSData?
public var d: NSData?
}

let obj = BasicD()
var arrayOfObjects: [String] = []
let mirror = Mirror(reflecting: obj)
for child in mirror.children {
guard let key = child.label else { continue }
arrayOfObjects.append(key)
print(key)
}
print(arrayOfObjects)

Here arrayOfObjects contains all your variable name of class BasicD



Related Topics



Leave a reply



Submit