How can I check if a variable is not initialized in Swift? - 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.

Related

How to check for nil inside extension

Following code
extension String {
func isValidEmail() -> Bool {
if self == nil { return false }
Gives me error Value of type 'String' can never be nil, comparison isn't allowed
Is it possible to somehow check for nil here?
Checking for nil there is not required.
That function is an instance function on String.
It can only ever be run on an instance of String.
Secondly Swift does not have "nil messaging" like Objective-C so the String instance that the function is called on HAS to be not nil. Even in Objective-C this would still not matter as the function would not run if called on a nil String.
So, the message is correct, Value of type "String" can never be nil.
You could check if your string is empty as:
var myString : String?
if myString.isEmpty {
// do whatever you want ..
}
That's has more sense..
Recall that in Swift, values of type String can never be nil; if you wanted a nillable value, you'd have to declare it String?.
So no, there is no way to check if a String variable is set to nil, because it can't.

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

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.

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.

Error message: initialiser for conditional binding must have optional type, not '()'

I am getting error:
initialiser for conditional binding must have optional type, not '()'.
Using Swift language, below is the code:
if let result = brain.performOperation(operation)
I think I can answer your question, but I don't know how much it will help you.
The way if let someVar = someOptional { } is used is to check the value of a variable (someOptional) to see if it is nil or a "value". If it is not nil then someVar will be assigned the non-nil value of someOptional and execute the code between { } which can safely reference someVar, knowing it is not nil. If someOptional is nil then the code within { } is bypassed and not executed.
The comment you posted above indicates that the performOperation() method is this:
func performOperation(symbol:String) {
if let operation = knownOps[symbol] {
opStack.append(operation)
}
}
This method does not return anything, or more formally it returns void aka (). void, or () is not a value, nor is it nil.
So when you have this statement
if let result = brain.performOperation(operation) { }
the compiler complains because it expects brain.performOperation(operation)
to return either nil or a value, but not void aka () which is exactly what the method returns.
If you are still confused about optionals, be sure to read as much as you can of the Swift Language Reference. Optionals are a big part of the language and once you get used to them you will find them very invaluable.

Reason for assigning optional to new variable in conditional statement in Swift

I'm going through the swift docs, and in the optional segment, it talks about using the question mark -- ? -- to signify variables that might be nil. This can be used in an if statement to check for nil, but in the docs they assign the optional to a new variable in the conditional. Is there a reason for this?
For Example, it is presented in the docs similar to this:
// Declare an optional string (might be nil)
var optionalString: String? = "Hello"
// Assigns optionalString to new variable before checking if nil
if let string = optionalString {
println("\(optionalString) is not nil!")
}
else {
println("\(optionalString) is nil")
}
However, this runs just fine for me in tests:
var optionalString: String? = "Hello"
// Assigns optionalString to new variable before checking if nil
if optionalString {
println("\(optionalString) is not nil!")
}
else {
println("\(optionalString) is nil")
}
Question
Is there a reason to assign optionalString to a new variable string in the conditional statement?
Take a look at the section on Optional Chaining in the docs. In the example you cite, there's not much difference. But in other cases, an if-let construction lets you get at an unwrapped value that comes from a series of optional references and method calls, without using implicit unwraps that can crash your app if you haven't considered all the possible bindings for a value in a chain.
It's also useful if you want to avoid recomputing a value. You can use it in a lot of the same ways you'd use an assignment in a conditional in (Obj)C (remember if (self = [super init])).
For example, if the optional being tested comes from a computed property:
var optionalName: String? {
get {
if checkTouchID() {
return "John Appleseed"
} else {
return nil
}
}
}
var greeting = "Hello!"
if optionalName != nil {
greeting = "Hello, \(optionalName)"
}
Paste that into a playground, along with a stub implementation of checkTouchID() that returns true, and you'll immediately see in the results area that the optionalName getter is executing twice. (This would be a problem in a more realistic scenario, because you probably don't want code like this to implicitly checkTouchID() or downloadFromServer() or billApplePay() twice.) If you use an if-let construction instead, you'll only execute the getter once.
In a series of chained optionals (like if let johnsStreet = john.residence?.address?.street in the docs linked above), you don't want to rewrite the whole chain in the body of the if statement, much less recompute it.
I think the purpose of that assignment was to demonstrate the use of "let" within the if conditional clause. I don't see a meaningful difference between the provided code and your own.
From: Apple Inc. “The Swift Programming Language.” iBooks. https://itun.es/il/jEUH0.l
“If the optional value is nil, the conditional is false and the code in braces is skipped. Otherwise, the optional value is unwrapped and assigned to the constant after let, which makes the unwrapped value available inside the block of code.”