Reason for assigning optional to new variable in conditional statement in Swift
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.
Best way to conditionally assign variable a value in swift
Use the nil-coalescing operator:
let value = someOptionalField ?? someDefaultValue
If someOptionalField
is nil, it automatically assigns value
to someDefaultValue
How is Swift `if let` evaluated?
Essentially the line is saying, "if you can let the new variable name
equal the non-optional version of optionalName
, do the following with it". As Martin pointed out, this is called Optional Binding.
The sole purpose of it is to test if an optional variable contains an actual value and bind the non-optional form to a temporary variable. This is the safe way to "unwrap" an optional or in other words, access the value contained in the optional. It is in no way testing for equality of any kind. It is only testing for the existence of a value within an optional.
Unwrapping optionals to a pre-defined variable in the guard condition without creating a new constant
I would just test for nil
and then force unwrap when I know it's not:
var someString: String? = "hello"
let nonOptionalString: String // note, you don't have to initialize this with some bogus value
guard someString != nil else { return }
nonOptionalString = someString!
Or if someString
was a parameter to some method or closure, you can unwrap in the guard
statement using the same variable name, simplifying life even more:
func foo(someString: String?) {
guard let someString = someString else { return }
// now I can just use local `someString`, which is not optional anymore
}
If you're desperate to unwrap and exit-if-nil
in a single statement, you could theoretically write a function to unwrap if it can or throw an error if it can't:
extension Optional {
enum OptionalError: Error {
case unwrapFailed
}
func unwrap<T>() throws -> T {
if self == nil { throw OptionalError.unwrapFailed }
return self as! T
}
}
Then you can do:
do {
firstNonOptional = try firstOptional.unwrap()
secondNonOptional = try secondOptional.unwrap()
thirdNonOptional = try thirdOptional.unwrap()
} catch {
return
}
I think that's horrible overkill, but if you're desperate to distill it down to one line per unwrap, that's one way to do it.
Why use an extra let statement here?
The if-let
construction is sort of superfluous in a simple case like this, but in a more complicated piece of code it can be useful. 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 is a computed property:
var optionalName: String? {
get {
if checkTouchID() {
return "John Appleseed"
} else {
return nil
}
}
}
var greeting = "Hello!"
if optionalName {
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. If you use an if-let
construction instead, you'll only execute the getter once.
This also true — and perhaps more commonly useful — if you have a series of chained optional calls, as in the if let johnsStreet = john.residence?.address?.street
example from the docs. You don't want to rewrite the whole chain in the body of the if
statement, much less recompute it.
Setting variable values via an IF statement in Swift
Instead of ==
you wrote =
in your if statement, and by the way here's a shorter version of that code
let locValueDeterminer = Int.random(in: 0...1)
let loc1Value = locValueDeterminer == 0 ? 0.5 : 0.0
let loc2Value = 1.0
Asking what locValueDeterminer == 0 ? 0.5 : 0.0
means?-
it's equivalent to condition ? something : something else
So in a way it translates to:
if locValueDeterminer == 0{
loc1Value = 0.5
}else{
loc1Value = 0.0
}
Why doesn't using LET as an optional throw an error, as it is an implicit comparison to zero - inconsistency in Swift Intro book?
You are kind of right, it is not consistent, it should not mention Boolean Expression but rather a Logic Value. Here is what the Language Reference says about if
:
The value of any condition in an if statement must have a type that conforms to the LogicValue protocol. The condition can also be an optional binding declaration, as discussed in Optional Binding.
Please note that optional binding here is not treated as an expression. It is not an assignment in if like in C. It is just a special case of an if
statement that succeeds when the bound value is not nil.
None of this is valid in Swift:
x = y = 10 // () is not convertible to Int
if z = optional { } // type () does not conform to LogicValue
if let z = optional && z > 5 { } // bound value must be of Optional type
if (let z = optional) && z > 5 { } // pattern variable binding cannot appear in expression
Edit:
At first my answer stated that "assignment is not an expression in Swift", but technically that is incorrect. Assignment is an expression, but of type ()
a.k.a. Void
. This is valid Swift:
var v = ()
var x = 0
v = x = 10 // x = 10; v = ()
Edit2:
Just to make this perfectly clear, when you use if let
or if var
it is not an assignment to an existing variable, a new variable/constant is introduced that is scoped to the inside of the if
block:
var x = 0
var y : Int? = 10
if var x = y { // new variable x is introduced
println(x) // prints 10
x = 20 // neither affects y nor 'outer' x
}
if let x = y { // new constant x is introduced
println(x) // prints 10
}
if let y = y { // introduce new y shadowing the original
println(y) // prints 10
}
println(x) // prints 0
//if x = y { } // does not compile
use of let in optional value along with if
Your second code snippet is functionally equivalent to the one from the book. However, the book suggests using let
to avoid multiple unwrapping of the optional value.
Since you are checking optionalName
for being empty, it is safe to assume that you plan to use it inside the conditional block. When you do so, Swift would need to unwrap the optional value from optionalName
again, because the variable remains optional.
When you do the assignment of optionalName
to name
in the let
declaration, the name
constant that you get back is not optional. This saves you the unwrap inside the if
statement.
Why can I declare a variable without writing optional mark?
Swift allows you to declare a non-optional variable or constant without initializing it in the declaration, but you will have to assign it a value before using it. The value of the variable or constant is not nil (non-optionals can never be nil)--it simply has no defined value. Basically you are saying you will give it a value later, possibly based on the result of a computation or an if statement.
Swift will give you a compile-time error if you try to use a non-optional variable or constant without first assigning a value to it.
Related Topics
When Should I Use Optionals and When Should I Use Non-Optionals with Default Values
Realm Mobile Platform, How to Connect While Offline
Why Does an @Objc Enum Have a Different Description Than a Pure Swift Enum
How to Determine If a Variable Passed in Is Reference Type or Value Type
How to Enumerate a Slice Using the Original Indices
Generate an Rsa Public/Private Key Pair
Changing Value in Nested Dictionary in Swift
Select All Text in Textfield Upon Click Swiftui
Ycombinator Not Working in Swift
Display All Available Wifi Connections with Swift in Os X
Why Do I Still Need to Unwrap Swift Dictionary Value
Difference Between Println and Print in Swift
How to Edit the Uiblureffect Intensity