3

Given var varName = "varValue", is there a way in Swift to convert the variable name to String at runtime? For example in this case I would get back "varName".

I already know about Mirror APIs for reflection in Swift. That allows me to iterate over the properties of a given class but I would like to apply this to self for any given class. I want to use this to generate String keys automatically for any given object (irrespective of which class it belongs to)

extension UIView {
   var key: String {
      return "" //TODO: Convert self to varName as String
   }
}

// Usage
let customView = UIView()
customView.key // should be "customView"
Numan Tariq
  • 1,296
  • 1
  • 9
  • 18
  • 1
    I don't know if this is possible, but I do know that this is not the way you should be tackling this. On runtime you just shouldn't care about variable names. – Sander Saelmans Oct 16 '18 at 14:14
  • `key` is not a `func` – ielyamani Oct 16 '18 at 14:20
  • @SanderSaelmans I am open to alternate suggestion which lets me have a unique constant key for the use case in the question. I don't care about the variable name. I care about a unique key in the given context – Numan Tariq Oct 16 '18 at 14:42
  • @NumanTariq If all you want is a unique key, `UIView` has a `tag` property for that exact purpose. – Johannes Fahrenkrug Oct 16 '18 at 14:45
  • @JohannesFahrenkrug I do not want to assign the unique key manually. I am trying to find a way for objects to return a unique key that does not change. – Numan Tariq Oct 16 '18 at 14:47
  • Something like: https://stackoverflow.com/a/22035149/1071489 would be ideal – Numan Tariq Oct 16 '18 at 14:52
  • What do you need the “key” for? – Martin R Oct 16 '18 at 15:13
  • In Swift, property names are purely a convenience for the programmer (so as to give human names to memory addresses), and they don't exist in the program at runtime. What actually are you trying to accomplish by this? There maybe other ways to achieve your goal. – nayem Oct 16 '18 at 15:21
  • I need the 'key' to be different for 2 instances of UIView. I want that key to be the same every time for that particular instance i.e. the key shouldn't change if the app is restarted or the view instance is destroyed and recreated. I can use this key as key in caching. Another use case can be to use it as accessibilityIdentifier to help with UITesting – Numan Tariq Oct 16 '18 at 15:26
  • @NumanTariq Ah, OK. You should update the question with these specific requirements. The best solution would be to subclass `UIView` and pass in the identifier you want each instance to have. That way you can set up the `accessibilityIdentifier` right in the init method. Also, as a general rule, you want to rather make things explicit and easy-to-understand than to use "magic". It's harder to debug and much harder and more unexpected for yourself and other developers to understand. – Johannes Fahrenkrug Oct 16 '18 at 15:53
  • @NumanTariq Why don't you just use the `.tag` property?? – ielyamani Oct 16 '18 at 15:55
  • "*I want that key to be the same every time for that particular instance i.e. the key shouldn't change if the app is restarted or the view instance is destroyed and recreated*" – but if the instance is destroyed and recreated, it isn't the same instance. Also what should happen for `let x = customView; x.key`? `x` points to the same instance as `customView`, but it's a different variable name. – Hamish Oct 16 '18 at 16:07
  • (on a tangent, purely for fun [a while ago I implemented a prototype](https://github.com/hamishknight/swift/commit/aa5eef4b61bd6863f08f1c5104bff325db5ca359#diff-79c589aa443482225d11fdd657f62f6eR1) for a magic literal `#name(x)` which would evaluate to a string literal of the name of the declaration `x` refers to – I'm not convinced it would make a great language feature though) – Hamish Oct 16 '18 at 16:14

2 Answers2

0

Update:

The OP added this comment clarifying the requirements:

I need the 'key' to be different for 2 instances of UIView. I want that key to be the same every time for that particular instance i.e. the key shouldn't change if the app is restarted or the view instance is destroyed and recreated. I can use this key as key in caching. Another use case can be to use it as accessibilityIdentifier to help with UITesting.

In that case, I suggest to not even think about using ✨magic✨. Just explicitly give your view instances an identifier. You could also totally just reuse existing properties on UIView like tag or accessibilityIdentifier. If that's not enough or not convenient enough, subclass:

class IdentifiableView: UIView {
    public private(set) var identifier: String

    init(frame: CGRect, identifier: String) {
        self.identifier = identifier
        super.init(frame: frame)
        self.accessibilityIdentifier = identifier
    }

    init() {
        fatalError("Must use init(frame:identifier:)")
    }

    override init(frame: CGRect) {
        fatalError("Must use init(frame:identifier:)")
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("Must use init(frame:identifier:)")
    }
}

// Usage
let firstView = IdentifiableView(frame: .zero, identifier: "First View")
firstView.identifier
firstView.identifier

let otherView = IdentifiableView(frame: .zero, identifier: "Second View")
otherView.identifier
otherView.identifier

If, according to your comment, you simply want "objects to return a unique key that does not change", you could simply use their memory address:

extension UIView {
    var key: String {
        return "\(Unmanaged.passUnretained(self).toOpaque())"
    }
}

let firstView = UIView()
firstView.key // -> "0x00007fbc29d02f10"
firstView.key // -> "0x00007fbc29d02f10"

let otherView = UIView()
otherView.key // -> "0x00007fbc29d06920"
otherView.key // -> "0x00007fbc29d06920"

For each instance of UIView you create this will return a unique value that will not change.

Johannes Fahrenkrug
  • 42,912
  • 19
  • 126
  • 165
-1

I'm not sure what you are trying to accomplish here but I think you can convert variable name to string in Swift with #keyPath(propertyName) but it requires you to add @objc to your var.

For example

class MyViewController : UIViewController {
    @objc var name: String = "John"
}

print(#keyPath(MyViewController.name))

prints name in the console.

Dávid Pásztor
  • 51,403
  • 9
  • 85
  • 116
lionserdar
  • 2,022
  • 1
  • 18
  • 21