3

I've got a texture to import that's named the same as the variable boolean that represents its state.

let myButtonNameABC = false

is there a way to convert the name of this variable to a String so it can be used to load the images that are named the same?

Confused
  • 6,048
  • 6
  • 34
  • 75
  • I think your answer is [here](http://stackoverflow.com/questions/26005654/swift-get-variable-actual-name-as-string) – Ray Toal Nov 20 '16 at 23:11
  • 1
    @RayToal it seems this it it... `NSPredicate(format: "%K == %@", #keyPath(Person.firstName), "Andrew")` but where is the variable name in this example, and where is it sent and stored? – Confused Nov 20 '16 at 23:16
  • 3
    I don't actually know for sure, since I only write pure Swift with no Objective-C interop. If you can interop with keyPath then great, and maybe someone else will answer here. But I wonder if the better answer is to not do this kind of thing in Swift. Rather than making a variable whose actual name you care about, consider a Dictionary where you have much easier access to key names without polluting the code with Objective-C? (Just a thought...) – Ray Toal Nov 20 '16 at 23:20
  • This would make for an interesting proposal. If you have anything you'd like to share, please start a discussion on swift-evolution requesting this feature. – CodaFi Nov 25 '16 at 07:40
  • Answer for your question: https://stackoverflow.com/a/45622489/1465582 – Irshad Mohamed Aug 10 '17 at 19:58

2 Answers2

3

You can make this a function instead of a variable, and do the same thing. I'll explain it in steps.

So first, how do we make a function the same thing as a variable?

Basically, we are "wrapping" the variable inside of the function. This is a similar to approach to the first block I suggested in your latest RefBool thread, where we grabbed or set a value via function.

Here, we are using methods/functions to alter a variable directly without parameters--so, we will be using the function by name instead of using the variable by name to access it throughout our code:

enum Static {
  private static var _button1 = false
  static func button1() { toggle(&_button1) }
}

Note, the Enum is just a namespace for us to call the method to emphasize that the variable's name is not needed for our code to work--Only the function needs to know its name.This code would work inside of a struct or class as well, or even in a global namespace.

So here, if I want to change the value of _button1, I must call button1(). _button1 is essentially invisible to everyone and everything--and that's ok, because we only need button1() to do our work.

So now, every time we call Static.button1() , Static._button1 will toggle true to false, false to true, and so on (using the function I gave you earlier)


Why are we doing this?

Well, because there is no way to get the name of a variable without Mirror and reflection, and there is already a built-in command called #function. We are jumping through the added hoop of using the function, so we don't have to set-up a mirror and other OBJc hoops.

Let's alter our enum to demonstrate how we can get the name of the func:

 enum Static {
  private static var _button1 = false
  
  static func button1() -> String { 
    toggle ( &_button1 ) 
    return ( #function )
  }
}

Now, when we call button1(), we do two things: 1, we toggle the state of the button (_button1), and we return a string with the name "button1()"

let buttonName = Static.button1()  // buttonName = "button1()"

We can make this more usable by calling a member of String to mutate itself:

let trimmedButtonName = Static.button1().replacingOccurrences(of: "()", with: "")
// trimmedButtonName = "button1"

Let's update our enum now with this handy replacement:

enum Static {
  private static var _button1 = false
  
  static func button1() -> String { 
    toggle ( &_button1 ) 
    return ( #function.replacingOccurrences(of: "()", with: "") )
  }
}

And our final use case is as follows:

let buttonName = Static.button1()

Note:

You don't have to have _button1 as private, that was just to demonstrate that you don't need it to be public. If you need to grab the state of it, you can make another function, or return a tuple type from button1():

static func button1() -> (name: String, state: Bool) {
// ...        
  return ( #function.etc, _button1)
}

let results = Static.button1()
print( results.name  ) // "button1"
print( results.state ) //  true / false

You can also set something in the parameter if you need more explicit control over setting the variable, just as you would any normal function.


UPDATE:

For example, if you wanted to use explicit control, you could do:

func button1(equals: Bool? = nil) -> (name: String, state: Bool) {
  if equals != nil {
    // do the code mentioned above
  } else {
    _button1 = equals!
  }

  return (#function.replacingOccurrences(of: "()", with: ""), _button1)
}

let buttonState.state = Static.button1(equals: false) // false
let buttonStateAgain.state = Static.button1(equals: true) // true
print(Static.button1().name) // "button1"
  
Community
  • 1
  • 1
Fluidity
  • 3,985
  • 1
  • 13
  • 34
  • 1
    This still doesn't address local variables. Plus it's a lot of boilerplate for something that should be... well, free. – CodaFi Nov 25 '16 at 07:42
  • 1
    @CodaFi please share your alternative – Fluidity Nov 25 '16 at 23:22
  • How about a [swift-evolution proposal](https://swift.org/contributing/)? – CodaFi Nov 25 '16 at 23:23
  • @CodaFi that's cool and all, but you are being critical of my workaround since it should be free--but it's not. I don't understand – Fluidity Nov 25 '16 at 23:27
  • I'm critical of this because it's legitimately a language limitation, and one that we are aware of. Stringification of variables is something any half-decent preprocessor is capable off; something that can be implemented in the compiler trivially. tl;dr Boilerplate is boilerplate, but this is something that merits a higher-level and more general solution. – CodaFi Nov 25 '16 at 23:29
  • @CodaFi so basically your criticism has nothing to do with my answer or my attempt to help the OP. I don't deserve my answers to be brought down for something inherently wrong with the language that I have no control over. 2c. – Fluidity Nov 25 '16 at 23:31
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/129077/discussion-between-codafi-and-fluidity). – CodaFi Nov 25 '16 at 23:32
3

You can use Mirror for this.

struct Test {
    let myButtonNameABC = false
}

let childrens = Mirror(reflecting: Test()).children

childrens.forEach {
    print($0.label ?? "")
}

This will print:

myButtonNameABC

You can find for information about Mirror here.

Lexorus
  • 199
  • 7
  • 2
    you would have to set up a lot of pattern matching to find just the variable you wanted though, wouldn't you? Or are you saying to make a new struct for each variable? – Fluidity Nov 25 '16 at 23:25