9

Is there a way to get list of variables and function of a class?

For example: If my class is like below

class Person {
    var age: Int!
    var name: String! 
    func isOlder(from person: Person) -> Bool { }
    func hasSameName(as person: Person) -> Bool { }
}

I want to get 2 lists:

1. [age, name]
2. [isOlder( _ : ), hasSameName( _ : )]

Or something similar.

Thanks

Saranjith
  • 11,242
  • 5
  • 69
  • 122
ilan
  • 4,402
  • 6
  • 40
  • 76
  • Hmm... not sure but you could check out the `Codable` protocol in iOS 11. Maybe? If there isn't another way. – Fogmeister Jun 21 '17 at 08:45

4 Answers4

10

Swift 4:

func printMethodNamesForClass(cls: AnyClass) {
    var methodCount: UInt32 = 0
    let methodList = class_copyMethodList(cls, &methodCount)
    if let methodList = methodList, methodCount > 0 {
        enumerateCArray(array: methodList, count: methodCount) { i, m in
            let name = methodName(m: m) ?? "unknown"
            print("#\(i): \(name)")
        }

        free(methodList)
    }
}
func enumerateCArray<T>(array: UnsafePointer<T>, count: UInt32, f: (UInt32, T) -> Void) {
    var ptr = array
    for i in 0..<count {
        f(i, ptr.pointee)
        ptr = ptr.successor()
    }
}
func methodName(m: Method) -> String? {
    let sel = method_getName(m)
    let nameCString = sel_getName(sel)
    return String(cString: nameCString)
}
func printMethodNamesForClassNamed(classname: String) {
    // NSClassFromString() is declared to return AnyClass!, but should be AnyClass?
    let maybeClass: AnyClass? = NSClassFromString(classname)
    if let cls: AnyClass = maybeClass {
        printMethodNamesForClass(cls: cls)
    } else {
        print("\(classname): no such class")
    }
}
Anton Plebanovich
  • 1,296
  • 17
  • 17
  • This works in Xcode 12 but only with class types. Methods are listed for subclasses of NSObject or methods which were marked with @objc – Blazej SLEBODA Dec 20 '20 at 16:17
5

In Objective C

#import <objc/runtime.h>

getDetailsOfClass(Class clz) {

    unsigned int methodCount = 0;
    Method *methods = class_copyMethodList(clz, &methodCount);

    printf("Found %d methods on '%s'\n", methodCount, class_getName(clz));

    for (unsigned int i = 0; i < methodCount; i++) {
        Method method = methods[i];

        printf("\t'%s' has method named '%s' of encoding '%s'\n",
               class_getName(clz),
               sel_getName(method_getName(method)),
               method_getTypeEncoding(method));
    }

    free(methods);
}

Invoke: getDetailsOfClass(class)

In Swift

Follow the SO Post. Have implemented same and found success.

To get Method names:

func printMethodNamesForClass(cls: AnyClass) {
    var methodCount: UInt32 = 0
    let methodList = class_copyMethodList(cls, &methodCount)
    if methodList != nil && methodCount > 0 {
        enumerateCArray(methodList, methodCount) { i, m in
            let name = methodName(m) ?? "unknown"
            println("#\(i): \(name)")
        }

        free(methodList)
    }
}
func enumerateCArray<T>(array: UnsafePointer<T>, count: UInt32, f: (UInt32, T) -> ()) {
    var ptr = array
    for i in 0..<count {
        f(i, ptr.memory)
        ptr = ptr.successor()
    }
}
func methodName(m: Method) -> String? {
    let sel = method_getName(m)
    let nameCString = sel_getName(sel)
    return String.fromCString(nameCString)
}
func printMethodNamesForClassNamed(classname: String) {
// NSClassFromString() is declared to return AnyClass!, but should be AnyClass?
let maybeClass: AnyClass? = NSClassFromString(classname)
if let cls: AnyClass = maybeClass {
printMethodNamesForClass(cls)
}
else {
println("\(classname): no such class")
}
}

Happy Coding..

Saranjith
  • 11,242
  • 5
  • 69
  • 122
  • @ilan Hope its working.. need any further explainations? – Saranjith Jun 21 '17 at 09:03
  • I got the properties but cant figure out how to get the methods – ilan Jun 21 '17 at 09:20
  • still not working, what is enumerateCArray? Can you help me with a method that will work in an UIViewController extension that class_copyMethodList will get self – ilan Jun 21 '17 at 11:29
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/147257/discussion-between-saranjith-and-ilan). – Saranjith Jun 21 '17 at 11:42
1

I believe you can use Mirror API for this: https://developer.apple.com/documentation/swift/mirror

New key-path API in Swift 4 might also be helpful: https://github.com/apple/swift-evolution/blob/master/proposals/0161-key-paths.md

Göksel Köksal
  • 233
  • 1
  • 9
0

Code refactor for @Anton Plebanovich's answer

func printMethodNamesForClassNamed(classname: String) {
    // NSClassFromString() is declared to return AnyClass!, but should be AnyClass?
    let maybeClass: AnyClass? = NSClassFromString(classname)
    if let cls: AnyClass = maybeClass {
        printMethodNamesForClass(cls: cls)
    } else {
        print("\(classname): no such class")
    }
}

func printMethodNamesForClass(cls: AnyClass) {
    var methodCount: UInt32 = 0
    let methodList = class_copyMethodList(cls, &methodCount)
    if let methodList = methodList{
        let arr = Array(arrayLiteral: methodList)
        for (i, m) in arr.enumerated(){
            let name: String = methodName(m: m.pointee) ?? "Not Known"
            print("#\(i): \(name)")
        }
        free(methodList)
    }
}

func methodName(m: Method) -> String? {
    let sel = method_getName(m)
    let nameCString = sel_getName(sel)
    return String(cString: nameCString)
}


black_pearl
  • 2,549
  • 1
  • 23
  • 36
  • this prints only `#0: init` for any class I send as argument. I'm on XCode 13.4.1. Is this a bug/ patch? – Aswath Aug 24 '22 at 09:21