Swift 2.0 Get Mirrored Superclass Properties

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

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

How can I find the type of a property dynamically in swift (Reflection/Mirror)?

You don't need to inherit from NSObject (unless you have a good reason to).

class Employee {
var id: String?
var someArray: [Employee]?
}

let employee = Employee()

for property in Mirror(reflecting: employee).children {
print("name: \(property.label) type: \(type(of: property.value))")
}

Output

name: Optional("id") type: Optional<String>
name: Optional("someArray") type: Optional<Array<Employee>>

This also works with Structs

Using getter/setter for property makes it not appear in reflection (mirror)

For the first problem I ended up using the following extension, which does see properties with getters/setters unlike Mirror:

extension NSObject {

func propertiesNames() -> [String] {

var count : UInt32 = 0
let classToInspect = type(of: self)

guard let properties : UnsafeMutablePointer <objc_property_t> = class_copyPropertyList(classToInspect, &count) else { return [] }

var propertyNames : [String] = []

let intCount = Int(count)

for i in 0..<intCount {

let property : objc_property_t = properties[i]
let propertyName = NSString(utf8String: property_getName(property))!

propertyNames.append(propertyName as String)
}

free(properties)

return propertyNames
}
}

As for the second issue I ended up copying each property over from the theme to the button as they are always the same. The goal was to avoid having to maintain a Theme class to bridge values every time something new is implemented in ZFButton.

Swift: How to check for base class and superclass on a subclass

You have to expose the contained type as a typealias. Here is the solution.

protocol BaseClass { }
class Person : BaseClass { }
class Parent : Person { }

class GenericClass<T: BaseClass> {
typealias Contained = T
}

let myGeneric = GenericClass<Parent>()
print("myGeneric is GenericClass<BaseClass>: \(type(of: myGeneric).Contained() is BaseClass)")

How iterate through all properties of different classes in Swift?

This is what reflection is for. You can add a property / method to the Car class that list all the properties of the current class, plus those of the superclass. When this property / method is dynamically applied to the subclasses, it will give what you are looking for.

We need an extension to Mirror that returns all properties of the current instance and those of its superclass recursively, taken from Martin R's answer:

extension Mirror {
func toDictionary() -> [String: Any] {
var dict = [String: Any]()

// Attributes of this class
for attr in self.children {
if let propertyName = attr.label {
dict[propertyName] = attr.value
}
}

// Attributes of superclass
if let superclass = self.superclassMirror {
for (propertyName, value) in superclass.toDictionary() {
dict[propertyName] = value
}
}

return dict
}
}

class Car: CustomDebugStringConvertible {
// ...

var debugDescription: String {
let dict = Mirror(reflecting: self).toDictionary()
return dict.map { "\($0.key) = \($0.value)" }.joined(separator: "\n")
}
}

Usage:

var allClassesArrays = [carrsArray, sportCarsArray, tankerCarsArray]
for car in allClassesArrays.flatMap({ $0 }) {
print(car.debugDescription, terminator: "\n\n")
}

You of course don't have to conform Car to CustomDebugStringConvertible or name the property debugDescription. I just find them convenient since the debugger will use the debugDescription when you type po car.

Swift Mirror reflection not returning properties on UIVIew

Not sure what you are trying to achieve but UIView inherits NSObject. Because of this, you have much of the objc runtime at your disposal. So as an alternative, you could do the following:

import UIKit

extension NSObject {
func propertysNames() -> [String]{
var count : UInt32 = 0
let classToInspect = self.dynamicType
let properties : UnsafeMutablePointer <objc_property_t> = class_copyPropertyList(classToInspect, &count)
var propertyNames : [String] = []
let intCount = Int(count)
for var i = 0; i < intCount; i++ {
let property : objc_property_t = properties[i]
let propertyName = NSString(UTF8String: property_getName(property))!
propertyNames.append(propertyName as String)
}
free(properties)
return propertyNames
}
}

print(UIView().propertysNames())
// prints: "["_mayRemainFocused", "_sensitivitySize", "skipsSubviewEnumeration", "viewTraversalMark", "viewDelegate", "monitorsSubtree", "backgroundColorSystemColorName", "currentScreenScale", "maskView", "_userInterfaceIdiom", "hash", "superclass", "description", "debugDescription", "gesturesEnabled", "deliversTouchesForGesturesToSuperview", "deliversButtonsForGesturesToSuperview", "_shouldReverseLayoutDirection", "leadingAnchor", "trailingAnchor", "leftAnchor", "rightAnchor", "topAnchor", "bottomAnchor", "widthAnchor", "heightAnchor", "centerXAnchor", "centerYAnchor", "firstBaselineAnchor", "lastBaselineAnchor", "_keyboardOrientation", "_touchForceObservable", "_inheritedRenderConfig", "_lightStyleRenderConfig", "_accessoryViewFrame", "unsatisfiableConstraintsLoggingSuspended", "hash", "superclass", "description", "debugDescription", "hash", "superclass", "description", "debugDescription", "userInteractionEnabled", "tag", "layer", "focused", "semanticContentAttribute", "interactionTintColor", "_layoutDebuggingIdentifier", "_countOfMotionEffectsInSubtree", "_maskView", "_ancestorDefinesTintColor", "_ancestorDefinesTintAdjustmentMode", "_presentationControllerToNotifyOnLayoutSubviews", "_rawLayoutMargins", "_inferredLayoutMargins", "_dontUpdateInferredLayoutMargins", "_tracksFocusedAncestors", "_countOfFocusedAncestorTrackingViewsInSubtree", "_mutableLayoutGuides", "_mutableLayoutArrangements", "_hiddenManagedByLayoutArrangementCount", "_pendingHiddenCount", "previewingSegueTemplateStorage", "_continuousCornerRadius", "_canBeParentTraitEnviroment", "_layoutEngine", "_boundsWidthVariable", "_boundsHeightVariable", "_minXVariable", "_minYVariable", "_internalConstraints", "_constraintsExceptingSubviewAutoresizingConstraints", "unsatisfiableConstraintsLoggingSuspended", "_shouldArchiveUIAppearanceTags", "_interactionTintColor", "_backdropMaskViewForGrayscaleTint", "_backdropMaskViewForColorTint", "_backdropMaskViewForFilters", "_backdropMaskViews", "_wantsGeometryChangeNotification", "contentSizeNotificationToken", "layoutMarginsGuide", "readableContentGuide", "hash", "superclass", "description", "debugDescription", "traitCollection", "preferredFocusedView", "center", "bounds", "transform", "collisionBoundsType", "collisionBoundingPath"]\n"

Also, I do see some weirdness applying your code to UIKit objects. Not sure what the variable is that causes it to fail. It seems to work fine on NSObject types written in Swift:

import UIKit

class Fruit {
var type=1
var name="Apple"
var delicious=true
}

var s = [String]()
for c in Mirror(reflecting: Fruit()).children
{
if let name = c.label{
s.append(name)
}
}

print(s)
// works: "["type", "name", "delicious"]\n"

class FruitNSObject: NSObject {
var type:NSNumber=1
var name:NSString="Apple"
var delicious=true
}

s = [String]()
for c in Mirror(reflecting: FruitNSObject()).children
{
if let name = c.label {
s.append(name)
}
}

print(s)
// works: "["type", "name", "delicious"]\n"

s = [String]()
for c in Mirror(reflecting: UIView()).children
{
if let name = c.label {
s.append(name)
}
}

print(s)
// doesn't work: "[]\n"

s = [String]()
for c in Mirror(reflecting: UIViewController()).children
{
if let name = c.label {
s.append(name)
}
}

print(s)
// doesn't work: "[]\n"

So either this is a bug or there is some limitation in the Swift <-> ObjC in the current version of Swift. Perhaps it has to do with what @user3441734 pointed out in his answer.

BTW, all code was run on the most current version of Xcode (that's 7.1.1) in a playground.

Will an array of a superclass contain subclasses as well?

Tree options here

A: Change the array type to Student

var students: [Student]()

B: Cast each element from the persons array to a Student. Ideally inside a for each person.

guard let student = person as? Student else {
return // or continue if inside a for loop
}

Then u will have access to that student variable.

C: Or if they might be students and persons at the same time in the array then

for person in persons {
switch person{
case let student as Student: //student case
//do something
default: //person case
//do something
}
}


Related Topics



Leave a reply



Submit