Reason for Assigning Optional to New Variable in Conditional Statement in Swift

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



Leave a reply



Submit