Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift Mirror reflection not returning properties on UIVIew

Trying to get all properties of UIView or UIViewController with the follownig:

func propertysNames()->[String]{
    var s = [String]()
    for c in Mirror(reflecting: self).children
    {
        if let name = c.label{
            s.append(name)
        }
    }
    return s
}

This works on UIVIewController, but UIView does not seem to return properties, any advice?

like image 325
MCMatan Avatar asked Nov 02 '15 13:11

MCMatan


2 Answers

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.

like image 185
mattr Avatar answered Nov 15 '22 07:11

mattr


import UIKit

let viewController = UIViewController()
let view = UIView()
class MyViewController: UIViewController {
    let i = 1
    let myView = MyView()
}
class MyView : UIView {
    let label = UILabel()
    let i = 1
}

Mirror(reflecting: MyViewController()).children.count   // 2
Mirror(reflecting: MyView()).children.count             // 0

You are right! Just fill the bug report ...

or it is sabotage, because i found this

extension UIView : _Reflectable {
}
like image 26
user3441734 Avatar answered Nov 15 '22 07:11

user3441734