Swift: get the compile time name of variable (referencing to a class) - swift

Is there a way to get the compile time name of a variable in Swift 2?
I mean the first variable name, which references to a new class instance, if any.
Here is a simple example:
public class Parameter : FloatLiteralConvertible {
var name:String?
var value:Double
// init from float literal
public required init (floatLiteral value: FloatLiteralType) {
self.value = Double(value)
self.name = getLiteralName()
}
func getLiteralName () -> String {
var literalName:String = ""
// do some magic to return the name
return literalName
}
}
let x:Parameter = 2.0
print(x.value) // this returns "2.0"
print(x.name!) // I want this to return "x"
I've already checked similar questions on that topic handling mirroring or objective-c reflections. But in all those cases, one can get only the property names in a class - in the example above name and value.
The same question has been asked in 2014 - Swift: Get Variable Actual Name as String
- and I hope, that since then there is a solution in swift 2.

No, there is no way to do that.
You have to understand that in the compiled state that variable usually does not exist. It can be optimized out or it is represented only as an item on the execution stack.
Even in languages with much better reflection that Swift has, usually you cannot inspect local variables.
To be honest, getting the name of a local variable dynamically has no practical use case.

Related

Is there a way to get the name of a key path as a string? [duplicate]

How can you get a string value from Swift 4 smart keypaths syntax (e.g., \Foo.bar)? At this point I'm curious about any way at all, does not matter if it's complicated.
I like the idea of type information being associated with smart key path. But not all APIs and 3rd parties are there yet.
There's old way of getting string for property name with compile-time validation by #keyPath(). With Swift 4 to use #keyPath() you have to declare a property as #objc, which is something I'd prefer to avoid.
A bit late to the party, but I've stumbled upon a way of getting a key path string from NSObject subclasses at least:
NSExpression(forKeyPath: \UIView.bounds).keyPath
Short answer: you can't. The KeyPath abstraction is designed to encapsulate a potentially nested property key path from a given root type. As such, exporting a single String value might not make sense in the general case.
For instance, should the hypothetically exported string be interpreted as a property of the root type or a member of one of its nested types? At the very least a string array-ish would need to be exported to address such scenarios...
Per type workaround. Having said that, given that KeyPath conforms to the Equatable protocol, you can provide a custom, per type solution yourself. For instance:
struct Auth {
var email: String
var password: String
}
struct User {
var name: String
var auth: Auth
}
provide an extension for User-based key paths:
extension PartialKeyPath where Root == User {
var stringValue: String {
switch self {
case \User.name: return "name"
case \User.auth: return "auth"
case \User.auth.email: return "auth.email"
case \User.auth.password: return "auth.password"
default: fatalError("Unexpected key path")
}
}
usage:
let name: KeyPath<User, String> = \User.name
let email: KeyPath<User, String> = \User.auth.email
print(name.stringValue) /* name */
print(email.stringValue) /* auth.email */
I wouldn't really recommend this solution for production code, given the somewhat high maintenance, etc. But since you were curious this, at least, gives you a way forward ;)
For Objective-C properties on Objective-C classes, you can use the _kvcKeyPathString property to get it.
However, Swift key paths may not have String equivalents. It is a stated objective of Swift key paths that they do not require field names to be included in the executable. It's possible that a key path could be represented as a sequence of offsets of fields to get, or closures to call on an object.
Of course, this directly conflicts with your own objective of avoiding to declare properties #objc. I believe that there is no built-in facility to do what you want to do.
Expanding on #Andy Heard's answer we could extend KeyPath to have a computed property, like this:
extension KeyPath where Root: NSObject {
var stringValue: String {
NSExpression(forKeyPath: self).keyPath
}
}
// Usage
let stringValue = (\Foo.bar).stringValue
print(stringValue) // prints "bar"
I needed to do this recently and I wanted to ensure that I get a static type check from the compiler without hardcoding the property name.
If your property is exposed to Objective-C(i.e #objc), you can use the #keyPath string expression. For example, you can do the following:
#keyPath(Foo.bar)
#keyPath(CALayer.postion)
See Docs

Swift, when referencing a class property, is it making a copy of the data?

So I'm a little confused because of conflicting information, just looking for some clarity regarding memory allocation for Class properties.
So here are my assumptions, please let me know if any of them are wrong:
In Swift, except for Classes and Functions, everything is passed by Value.
Classes instances (objects) are allocated on the Heap
When you pass an object around, you are passing the pointer
When you reference a property on an object, the pointer is dereferenced, and the value of the property is retrieved
So here's my confusion, say my class has a String property, and an Int property. Both Swift data types, that get passed by value in any ordinary situation.
If I ask for let test = object.stringProperty, am I going to get a copy of my string value copied into my test variable?
Similarly, if I had a method inside of my class,
func getAllProperties() -> (String, Int) {
return (self.stringProperty, self.intProperty)
}
is object.getAllProperties() going to return a copy of the properties in a tuple?
I know it seems like a basic question, but after reading several sources I just ended up more uncertain than when I started
Yes and yes. It doesn't matter that the String and the Int were in a class. You asked for the String or the Int (or both), those are value types, you got copies.
It's easy to prove this to yourself, especially with the String. Just change something about it, and then look back at what the class instance is holding: it will be unchanged.
class C {
var stringProperty : String
init(string:String) {
self.stringProperty = string
}
}
let c = C(string:"hello")
var s = c.stringProperty
s.removeLast()
print(s) // hell
print(c.stringProperty) // hello
If you want to see the class-as-reference in action, make two of the same instance and do something to one of those:
class C {
var stringProperty : String
init(string:String) {
self.stringProperty = string
}
}
let c = C(string:"hello")
let d = c
c.stringProperty = "goodbye"
print(d.stringProperty) // goodbye

What's the point of READ-only variables when you have LET?

For example:
var dogName : String {
return "Buster"
}
VS..
let dogName = "Buster"
Let's say we're declaring each of these at the top level of a class as instance properties. Are these just two ways of doing the same thing? If not, what's the point of having a read-only variable?
Thanks
Let me try to sum up what the other answers are saying while also adding missing information that I think is critical in order to understand this.
Properties
Properties are simply values that are associated with an object and may be queried in a trivial amount of time without the need (or ability) for parameters like methods have.
Stored Properties
When you create a stored property, whether with let or var, the value assigned at any given point in time will be stored in memory, which is why it is called a stored property.
var name = "Matt"
For variables using var, the value is stored in memory in a way that makes it mutable (editable). You can reassign the value at will and it will replace the previous value stored in memory.
let name = "Matt"
For constants using let, the value is also stored in memory, but in such a way that it may not be changed after the first time assigning to it.
Computed Properties
Computed properties are not stored in memory. As ganzogo says in the comments, computed properties act similarly to methods, but do not take parameters. When deciding when to use a computed property or a function with no parameters, the Swift API Design Guidelines recommend using a computed property when it will simply create or fetch, and then return the value, provided that this takes a trivial amount of time.
var fullName: String {
return firstName + lastName
}
Here, we assume that firstName and lastName are already properties on the object. There is no sense of initialization with this property because it is not stored anywhere. It is fetched on demand every time. That is why there is no sense to doing anything like the following:
var dogName : String {
return "Buster"
}
This has no benefit over a stored property except that no memory will be used in storing the String "Buster".
In fact, this is a simplified version of computed properties. You will notice that the Swift Language Guide describes the use of both get and set in a computed property. set allows you to update the state of other variables when one sets a computed variable. For example:
var stored: Int
var computed: Int {
get {
return stored + 5
}
set {
stored = newValue - 5
}
}
Some useful applications of this were pointed out by Rajan's answer, for example getting and setting volume from width, height, and depth.
A read-only computed var is just a computed var which specifies only a getter, in which case the get keyword and brackets are not required.
Read-Only for Access Control
When developing modules such as frameworks, it is often useful to have a variable only be modifiable from within that object or framework and have it be read-only to the public.
private var modifiableItem: String
public var item: String {
return modifiableItem
}
The idea here is that modifiableItem should only be mutable from within the object that defined it. The private keyword ensures that it is only accessible within the scope of the object that created it and making it a var ensures that it may be modified. The public var item, then, is a computed variable that is exposed to the public that enables anyone to read, but not mutate the variable.
As Hamish notes in the comments, this is more concisely expressible by using private(set):
public private(set) var item: String
This is probably the best way to go about it, but the previous code (using a private stored property and public computed one) demonstrates the effect.
let dogName = "Buster"
means that the dogName variable can't be changed later on once assigned "Buster" and it becomes constant
var dogName : String {
return "Buster"
}
It is a computed read only property where you can have some calculation which can be changed as it is a var but in a way defined below:
The computed property can be changed like
var dogName : String {
return "Stress"+"Buster"
}
Consider this example from Apple Docs
struct Cuboid {
var width = 0.0, height = 0.0, depth = 0.0
var volume: Double {
return width * height * depth
}
}
let fourByFiveByTwo = Cuboid(width: 4.0, height: 5.0, depth: 2.0)
print("the volume of fourByFiveByTwo is \(fourByFiveByTwo.volume)")
It will print
// Prints "the volume of fourByFiveByTwo is 40.0"
Here the volume is calculated when you initialize the object of struct Cuboid and is computed at run time. If it was let, then you have to initialize it before using by some constant.
If you want to read more about it, read the Computed Properties section here
In your example, they are 2 ways of doing the same thing. However, you can do a lot more with a computed property. For example:
var dogName: String {
return firstName + " " + lastName
}
Here, firstName and lastName might not be known at initialization time. This is not possible to do with a simple let property.
It might help you to think of a computed property as a method with no parameters.
A read-only property in a class/struct means that you can't change the value of the property for that instance of the class/struct. It prevents me from doing:
someObject.dogName = "Buddy" // This fails - read-only property
However, I can still do this:
var someVariable = someObject.dogName // someVariable now is "Buster"
someVariable = "Buddy" // This is OK, it's now "Buddy"
A let constant means you won't be changing the value of that specific constant in that block of code.
let someName = "Buster"
someName = "Buddy" // This fails - someName is a constant
There are two different cases:
1) Value type:
struct DogValueType {
var name: String
}
let dog1 = DogValueType(name: "Buster")
var dog2: DogValueType {
return DogValueType(name: "Buster")
}
let dog3: DogValueType = {
return DogValueType(name: "Buster")
}()
dog1 - dog3 can't be changed or mutated
dog1 & dog3 stores value
dog3 computes value each time you accessing it
2) Reference type:
class DogReferenceType {
var name: String
init(name: String) {
self.name = name
}
}
let dog4 = DogReferenceType(name: "Buster")
var dog5: DogReferenceType {
return DogReferenceType(name: "Buster")
}
let dog6: DogReferenceType = {
return DogReferenceType(name: "Buster")
}()
dog4 - dog6 can't be changed, but can be mutated
dog4 & dog6 stores reference to an object.
dog5 creates object each time you accessing it

Initializing class constants in Swift

I was trying to do something like this (it is a contrived example for demonstration purposes only):
class Test {
let hello = "hello"
let world = "world"
let phrase: String {
return self.hello + self.world
}
}
but you can't use let for computed properties in Swift. Is there a way to do this without having to write an init() method? Thanks!
The reason let doesn't work on a read-only calculated property is because it's used to state that the property's actual value will never change after being set – not that the property is read-only. As the Apple docs say (emphasis mine):
You must declare computed properties — including read-only computed
properties — as variable properties with the var keyword, because their
value is not fixed. The let keyword is only used for constant
properties, to indicate that their values cannot be changed once they
are set as part of instance initialization.
You therefore need to use var in order to reflect the fact that a calculated property's value could change at any time, as you're creating it on the fly when accessing it. Although in your code, this can't happen – as your hello and world properties are let constants themselves. However, Swift is unable to infer this, so you still have to use var.
For example:
class Test {
let hello = "hello"
let world = "world"
var phrase: String {
return self.hello + self.world
}
}
(This doesn't change the readability of the property – as because you haven't provided it with a setter, it's still read-only)
However in your case, you might want to consider using a lazy property instead, as your hello and world properties are constants. A lazy property is created when it's first accessed, and keeps its value for the rest of its lifetime – meaning you won't have to keep on concatenating two constants together every time you access it.
For example:
class Test {
let hello = "hello"
let world = "world"
lazy var phrase: String = {
return self.hello + self.world
}()
}
Another characteristic of let properties is that their value should always be known before initialisation. Because the value of a lazy property might not be known before then, you also need to define it as a var.
If you're still adamant on wanting a let property for this, then as far as I can see, you have two options.
The first is the neatest (although you've said you don't want to do it) – you can assign your phrase property in the initialiser. As long as you do this before the super.init call, you don't have to deal with optionals. For example:
class Test {
let hello = "hello"
let world = "world"
let phrase: String
init() {
phrase = hello+world
}
}
You simply cannot do it inline, as self at that scope refers to the static class, not an instance of the class. Therefore you cannot access the instance members, and have to use init() or a lazy/calculated property.
The second option is pretty hacky – you can mirror your hello and world properties at class level, so you can therefore access them inline in your phrase declaration. For example:
class Test {
static let hello = "hello"
static let world = "world"
// for some reason, Swift has trouble inferring the type
// of the static mirrored versions of these properties
let hello:String = Test.hello
let world:String = Test.world
let phrase = hello+world
}
If you don't actually need your hello or world properties as instance properties, then you can just make them static – which will solve your problem.
Yes to make it work as computed properties, replace let to var.
Like,
class Test {
let hello = "hello"
let world = "world"
var phrase: String {
return self.hello + self.world
}
}
This way you can use it without init()

Why does Swift BooleanLiteralConvertible require a boolean literal?

I am trying to add BooleanLiteralConvertible support to my class so I can instantiate it with a boolean. The thing that's throwing me for a loop is the distinction between a boolean value and a boolean literal.
For example, after adding the protocol I attempted this:
func setSelected(value: Bool) {
var node: MyClass = value
}
But Swift complained that it cannot convert Bool to MyClass. It took me a while to realize it has to be a boolean literal. Oddly enough the following works fine:
func setSelected(value: Bool) {
var node: MyClass = value ? true : false
}
…which seems just absolutely silly to me. Is there a legitimate reason for this seemingly very bizarre requirement?
Types conforming to BooleanLiteralConvertible can be initialized with the Boolean literals true and false, e.g.
let mc : MyClass = true
This has nothing to do with initializing the type with a Boolean value:
let value : Bool = // ... some boolean value
let mc : MyClass = value // error: cannot convert value of type 'Bool' to specified type 'MyClass'
and there is – as far as I know – no way to make such an implicit
conversion work. You would have to write a custom init method
init(bool : Bool) {
// ...
}
and initialize the object as
let value : Bool = // ... some boolean value
let mc = MyClass(bool: value)
I like the question. Only the Swift team could definitively answer, but I can speculate as to why: converting a typed value into a variable of a different type without an explicit conversion or cast is very easy to confuse with a programmer error, and in many cases is something the compiler should warn about.
Example (and assume that Person is also a StringLiteralConvertible that can be initialized with a string variable as well as a literal as you pose in your question):
struct Person {
private static var idCounter = 1
var name:String
let id:Int
init(withName name:String) {
Person.idCounter += 1
self.name = name
self.id = Person.idCounter
}
}
var person = Person(withName:"Mary")
let name = "John"
person = name
The above code looks suspiciously like a mistake, where the programmer is assigning a value of the wrong type (String) to a variable of type Person. It may in fact be a mistake. Maybe the programmer only meant to change the name of the person (person.name = name) without creating a new Person with a new unique id. Or maybe the programmer intended to assign some other value to person but made a typo or code completion error. Hard to tell without either being the original programmer, or carefully studying all the context to see whether this conversion makes sense. And it gets harder the further the assignment is from the place where the variables are originally initialized Should the compiler warn here that a value of type String is being assigned to a variable of type Person?
The example would be far more clear, and more in line with Swift conventions as:
var person = Person(withName:"Mary")
let name = "John"
person = Person(withName:name)
The above version is completely unambiguous, both to the compiler and to any other programmers who read this later.