1

I'm writing a base class that implements KVO, and I would like to infer the names of the dynamic attributes of the class. For example:

class BaseClass {
  func beginObserving() {
    // What are the dynamic attributes to observe?
    // In the case of "SubClass" below, it should be ["name"]
    let attributes = ???
    for attribute in attributes {
      self.addObserver(self, forKeyPath: attribute, options: [.New, .Old], context: &KVOContext)
    }
  }
}

class SubClass : BaseClass {
  dynamic var name: String!
}

List of class's properties in swift explains how to do this with Mirror (reflection), but it does not appear to work for dynamic vars (i.e., if I removed the dynamic keyword the linked code would work for this case).

Community
  • 1
  • 1
Zane Claes
  • 14,732
  • 15
  • 74
  • 131

1 Answers1

1

You can use the standard <objc/runtime.h> functions:

class MyObject {
    dynamic var identifier = 0

    func dynamicProperties() -> [String] {
        var dynamicProperties = [String]()

        var propertyCount = UInt32(0)
        let properties = class_copyPropertyList(object_getClass(self), &propertyCount)
        for var i = 0; i < Int(propertyCount); i++ {
            let property = properties[i]
            let propertyName = property_getName(property)
            dynamicProperties.append(String(CString: propertyName, encoding: NSUTF8StringEncoding)!)
        }
        free(properties)

        return dynamicProperties
    }
}

--

If you want subclasses to show not only their properties, but also those of its superclass, you can do the following:

class BaseClass {
    dynamic var identifier = 0

    func dynamicProperties() -> [String] {
        var dynamicProperties = [String]()

        var propertyCount = UInt32(0)
        let properties = class_copyPropertyList(object_getClass(self), &propertyCount)
        for var i = 0; i < Int(propertyCount); i++ {
            let property = properties[i]
            let propertyName = property_getName(property)
            dynamicProperties.append(String(CString: propertyName, encoding: NSUTF8StringEncoding)!)
        }
        free(properties)

        return dynamicProperties
    }
}

class SubClass : BaseClass {
    dynamic var name: String!

    override func dynamicProperties() -> [String] {
        var dynamicProperties = super.dynamicProperties()

        var propertyCount = UInt32(0)
        let properties = class_copyPropertyList(object_getClass(self), &propertyCount)
        for var i = 0; i < Int(propertyCount); i++ {
            let property = properties[i]
            let propertyName = property_getName(property)
            dynamicProperties.append(String(CString: propertyName, encoding: NSUTF8StringEncoding)!)
        }
        free(properties)

        return dynamicProperties
    }

}
Rob
  • 415,655
  • 72
  • 787
  • 1,044
  • This does not work. It returns `[]` for a subclass which has dynamic properties `dynamic var name: String!`, and `["x", "y"]` for a subclass which has normal lets `let x: UInt`, `let y: Uint`. Even your sample case does not output the `identifier` variable. Your code is the same solution as I linked to in my original question. – Zane Claes Nov 23 '15 at 19:45
  • No, it returns `["name"]` for a subclass that has a dynamic property of `dynamic var name: String!`. It only returns the properties for that class, not its super class(es). If you want all of them, you need to have the subclass then append its properties to those of its superclass, as shown in revised answer. – Rob Nov 23 '15 at 20:03