Constant property can be modified during initialization? MODIFIED? What? - swift

Quote from the Swift 3.0 office document of the Chapter: Initialization
For class instances, a constant property can be modified during initialization only by the class that introduces it. It cannot be modified by a subclass.
To my understanding the modified involves the action after the definition, aka the action after declaring and assigning value, aka re-assigning values, therefore I tried the following code.
class SurveryQuestion {
let text: String
var response: String?
init(text: String) {
self.text = "do you like music?"
self.text = text //Got an error here
}
func ask(){
print(text)
}
}
And I got an error at line self.text = text. The compiler asked me to change the property textfrom constant to variable. Isn't it says that the constant property can be modified by the initializer of the class which originally introduced it?
Question: Am I understand the word modified wrongly? Is it means the action after the declaring rather than the definition which would lead to the modified is meant to by passing a value to the constant.

I think that the documentation is not clear enough. You can set a constant property only once during initializing. You also would not be able to set it during initialization if the property's value was defined inline. Here is example.
class SomeClass {
let someProperty: String = "A"
init() {
self.someProperty = "" //ERROR: Immutable value "self.someProperty" may only be initialized once.
}
}
The compile time error //ERROR: Immutable value "self.someProperty" may only be initialized once. actually explains it well.

Related

String property returning different values when used directly or in string interpolation

I've just finished debugging a situation where some of my labels were not displaying any text, despite the string in question definitely containing a value (it was printing the line before). I eventually pinned it down to returning different values depending on the way I accessed the string. Please note I'm not looking for ways to work around this, I am looking for a reason I am missing as to why this behaviour happens.
I had a protocol with the following optional String property, set to default to nil:
protocol SomeProtocol {
var titleString: String? { get }
}
extension SomeProtocol {
var titleString: String? {
return nil
}
}
Which was implemented in a class with a not optional String, with title set elsewhere:
class SomeClass: SomeProtocol {
var title: String
var titleString: String {
return title
}
}
When attempting to access the value of titleString from a SomeClass object, it always returned nil, regardless of the title property. This was seen through assigning to a label like:
label.text = someClassInstance.titleString
However, when I printed the value or set the label through
label.text = "\(someClassInstance.titleString)"
everything worked, and it displayed the text.
I've narrowed the source of this down to where I've overridden the optional property with one not optional. Clearly when I access the property directly it is returned by the protocol implementation, while using it by string interpolation returns the class one. What is actually behind this behaviour?
Edit: to demonstrate this behaviour run this gist in an Xcode playground:
https://gist.github.com/CaileanWilkinson/357c17f36d04b522b9bcf1241a825d9f
I feel like this is somewhat similar to the solution of this question of mine. In that question, there is also two almost identical properties - one optional, the other non-optional. And I experienced a similar situation where Swift can't figure out which property I want.
Your titleString in SomeClass is not overriding the titleString property in the protocol. This is reflected in Xcode's suggestions:
You can access both properties like this:
someObject.titleString as String // accesses the one in SomeClass
someObject.titleString as String? // accesses the one in the protocol
My point here is that the type of the expression matters. If the type of expression swift expects is String, then it resolves to the one in SomeClass. If the expected type of the expression is String?, then it evaluates to the one in the protocol.
This explains why setting the label's text without string interpolation will call the property in the protocol (label.text is String?, so it expects a String?) and why using string interpolation will call the property in SomeClass (String interpolation expects a non-optional).

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

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.

Why doesn't Swift allow setting value of an optional constant after object initialization?

The code below creates a compile error saying "error: return from initializer without initializing all stored properties ('self.response' not initialized)"
class Question {
let text: String
let response: String?
init(text: String) {
self.text = text
}
func ask() {
print(text)
}
}
I want to make "response" constant and by the time I initialize, response will be unknown. Besides "return from initializer without initializing all stored properties", why do I have to make it "var"?
Because Swift tries to make you implement safe code, and having uninitialized stored properties is really not safe, because you or a client of your class may use that constant before it is properly set and the result will be undefined. This is a cause of a lot of bugs that may not be immediately caught.
Moreover, because an optional constant stored property is initialized as having a nil value, if you were able to change its value after initialization you would violate the "constantness" of your constant. That is why you need to declare it as a var.
Optional variables / properties are automatically set to nil by definition if no initial value is provided in the declaration line.
An optional constant is stuck to nil which makes no sense...
Therefore the compiler doesn't let you declare an optional constant this way.

How can I check if a variable is not initialized in Swift?

Swift allows variables to be declared but not initialized. How can I check if a variable is not initialized in Swift?
class myClass {}
var classVariable: myClass // a variable of class type - not initialized and no errors!
//if classVariable == nil {} // doesn't work - so, how can I check it?
You're right—you may not compare a non-optional variable to nil. When you declare, but do not provide a value for, a non-optional variable, it is not set to nil like an optional variable is. There is no way to test for the use of an uninitialized non-optional variable at runtime, because any possibility of such use is a terrible, compiler-checked programmer error. The only code that will compile is code that guarantees every variable will be initialized before its use. If you want to be able to assign nil to a variable and check its value at runtime, then you must use an optional.
Example 1: Correct Usage
func pickThing(choice: Bool) {
let variable: String //Yes, we can fail to provide a value here...
if choice {
variable = "Thing 1"
} else {
variable = "Thing 2"
}
print(variable) //...but this is okay because the variable is definitely set by now.
}
Example 2: Compilation Error
func pickThing2(choice: Bool) {
let variable: String //Yes, we can fail to provide a value here, but...
if choice {
variable = "Thing 1"
} else {
//Uh oh, if choice is false, variable will be uninitialized...
}
print(variable) //...that's why there's a compilation error. Variables ALWAYS must have a value. You may assume that they always do! The compiler will catch problems like these.
}
Example 3: Allowing nil
func pickThing3(choice: Bool) {
let variable: String? //Optional this time!
if choice {
variable = "Thing 1"
} else {
variable = nil //Yup, this is allowed.
}
print(variable) //This works fine, although if choice is false, it'll print nil.
}
It might be a anomaly of the compiler that you don't get an error declaring a variable this way
class MyClass {}
var myClass : MyClass
but in a Playground you get a runtime error when you just read the variable
myClass
variable 'myClass' used before being initialized
One of the most essential features of Swift is that a non-optional variable can never be nil. If you try to access the variable you'll get a runtime error aka crash.

Changing superclass variable in Swift

I read this sentence in the Swift documentation provided by Apple
“Subclasses are only allowed to modify variable properties of superclasses during initialization.”
Excerpt From: Apple Inc. “The Swift Programming Language.” iBooks.
But if I run this code it works with no problems:
class Person{
var name:String
init(name:String){
self.name = name
}
}
class SuperHero:Person{
var power:String?
init(name:String, power:String?){
self.power = power
super.init(name: name)
}
func changeName (){
// HERE I'M CHANGING A SUPERCLASS VARIABLE outside Initialization!!!
self.name = "Mark"
}
}
let superman = SuperHero(name: "Superman", power: "Fly!")
superman.changeName()
println("\(superman.name)")
Have I misinterpreted the documentation?
there is serious misinterpretation in the quoted sentence. that is the original quote in its natural environment.
Subclasses are only allowed to modify variable properties of superclasses during initialization. You can’t modify inherited constant properties of subclasses.
please read in with the correct emphasis, because that sentence means that on other words:
you are allowed to modify the variables of superclass during initilaization, but you are not allowed to do such thing with constants of superclass.
there is no such statement here: you are allowed to modify the variables only during initialization but and later you are not allowed to modify them.
I hope that makes sense now.
Sentence from Swift Language Guide is this:
Subclasses are only allowed to modify variable properties of superclasses during initialization.
Note that "variable" word is in italics. It means that there is an emphasis on this word, and the resulting sentence means that during initialization you can modify only those properties of superclass that are variable.
In expansion of my comment, the book is saying you can't do this:
class Person{
let name:String
init(name:String){
self.name = name // This is fine, but see warnings below
}
}
class SuperHero:Person{
var power:String?
init(name:String, power:String?){
self.power = power
super.init(name: name) // WARNING: Cannot assign to 'name' in 'self'
}
func changeName (){
// HERE I'M CHANGING A SUPERCLASS VARIABLE outside Initialization!!!
self.name = "Mark" // WARNING: Cannot assign to 'name' in 'self'
}
}
let superman = SuperHero(name: "Superman", power: "Fly!")
superman.changeName()
println("\(superman.name)")
As I commented above, the sentence only makes sense when paired with the following one: “You can’t modify inherited constant properties of subclasses.” It's not really saying that you can modify variables only during initialization it's saying you can't modify inherited constants during initialization of a subclass (nor at any point). The word initialization distracts from the point being made.