Get a Swift Variable's Actual Name as String

SwiftReflection

Swift Problem Overview


So I am trying to get the Actual Variable Name as String in Swift, but have not found a way to do so... or maybe I am looking at this problem and solution in a bad angle.

So this is basically what I want to do:

var appId: String? = nil

//This is true, since appId is actually the name of the var appId
if( appId.getVarName = "appId"){
    appId = "CommandoFurball"
}

Unfortunately I have not been able to find in apple docs anything that is close to this but this:

varobj.self or reflect(var).summary 

however, this gives information of what is inside the variable itself or the type of the variable in this case being String and I want the Actual name of the Variable.

Swift Solutions


Solution 1 - Swift

This is officially supported in Swift 3 using #keyPath()

https://github.com/apple/swift-evolution/blob/master/proposals/0062-objc-keypaths.md

Example usage would look like:

NSPredicate(format: "%K == %@", #keyPath(Person.firstName), "Wendy")

In Swift 4 we have something even better: \KeyPath notation

https://github.com/apple/swift-evolution/blob/master/proposals/0161-key-paths.md

NSPredicate(format: "%K == %@", \Person.mother.firstName, "Wendy")

// or

let keyPath = \Person.mother.firstName
NSPredicate(format: "%K == %@", keyPath, "Andrew")

The shorthand is a welcome addition, and being able to reference keypaths from a variable is extremely powerful

Solution 2 - Swift

As per the updated from this answer, it is supported in Swift 3 via #keyPath

NSPredicate(format: "%K == %@", #keyPath(Person.firstName), "Andrew")

Solution 3 - Swift

This is my solution

class Test { 
    var name: String = "Ido"
    var lastName: String = "Cohen"
}

let t = Test()
let mirror = Mirror(reflecting: t)

for child in mirror.children {
    print(child.label ?? "")
}

print will be

name
lastName

Solution 4 - Swift

This works:

struct s {
    var x:Int = 1
    var y:Int = 2
    var z:Int = 3
}

var xyz = s()

let m = Mirror(reflecting: xyz)

print(m.description)
print(m.children.count)
for p in m.children {
    print(p.label as Any)
}

Solution 5 - Swift

I've come up with a swift solution, however unfortunately it doesn't work with Ints, Floats, and Doubles I believe.

func propertyNameFor(inout item : AnyObject) -> String{
    let listMemAdd = unsafeAddressOf(item)
    let propertyName =  Mirror(reflecting: self).children.filter { (child: (label: String?, value: Any)) -> Bool in
        if let value = child.value as? AnyObject {
            return listMemAdd == unsafeAddressOf(value)
        }
        return false
        }.flatMap {
            return $0.label!
    }.first ?? ""
    
    return propertyName
}

var mutableObject : AnyObject = object
let propertyName = MyClass().propertyNameFor(&mutableObject)

It compares memory addresses for an object's properties and sees if any match. The reason it doesn't work for Ints, Floats, and Doubles because they're not of type anyobject, although you can pass them as anyobject, when you do so they get converted to NSNumbers. therefore the memory address changes. they talk about it here.

For my app, it didn't hinder me at all because I only needed it for custom classes. So maybe someone will find this useful. If anyone can make this work with the other datatypes then that would be pretty cool.

Solution 6 - Swift

Completing the accepted answer for extensions:

  1. The property needs to be @objc.
    var appId: String? {
    ....
    }
    
  2. You need to use #keyPath syntax, \ notation is not supported yet for extensions.

#keyPath(YourClass.appId)

Solution 7 - Swift

The best solution is Here

From given link

import Foundation

extension NSObject {
//
// Retrieves an array of property names found on the current object
// using Objective-C runtime functions for introspection:
// https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtPropertyIntrospection.html
//
func propertyNames() -> Array<String> {
    var results: Array<String> = [];

    // retrieve the properties via the class_copyPropertyList function
    var count: UInt32 = 0;
    var myClass: AnyClass = self.classForCoder;
    var properties = class_copyPropertyList(myClass, &count);

    // iterate each objc_property_t struct
    for var i: UInt32 = 0; i < count; i++ {
        var property = properties[Int(i)];

        // retrieve the property name by calling property_getName function
        var cname = property_getName(property);

        // covert the c string into a Swift string
        var name = String.fromCString(cname);
        results.append(name!);
    }

    // release objc_property_t structs
    free(properties);

    return results;
}

}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionS.H.View Question on Stackoverflow
Solution 1 - SwiftAndrewView Answer on Stackoverflow
Solution 2 - SwiftColinEView Answer on Stackoverflow
Solution 3 - SwiftIdo CohenView Answer on Stackoverflow
Solution 4 - SwiftcdeerinckView Answer on Stackoverflow
Solution 5 - SwiftDavid ReesView Answer on Stackoverflow
Solution 6 - SwiftCalin DruleView Answer on Stackoverflow
Solution 7 - SwiftDattatray DeokarView Answer on Stackoverflow