110

I've seen this code on the Swift site and various posts here and I am trying to grasp the basics. How is this line evaluated?

if let name = optionalName {

I'm confused as it's not name == optional name, it's assigning the value, so how does that report true and why is it not true when you replace with john appleseed with nil, as its still going to be equal?

var optionalName: String? = "John Appleseed"
var greeting = "Hello!"
if let name = optionalName {
    greeting = "Hello, \(name)"
}
mfaani
  • 33,269
  • 19
  • 164
  • 293
DeadZero
  • 1,261
  • 3
  • 11
  • 15
  • 12
    Lookup "optional binding" in the Swift documentation... – Martin R Sep 13 '14 at 22:26
  • 3
    A detailed looked at optionals at http://dev.iachieved.it/iachievedit/?p=314, the `if let` syntax is known as optional binding. – Joe Sep 13 '14 at 23:05

6 Answers6

128

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.

drewag
  • 93,393
  • 28
  • 139
  • 128
  • 3
    I will have a read of that when I get down to it , Im just on the start of the swift intro and it does not explain it. you explaination makes perfect sense, Thanks. – DeadZero Sep 15 '14 at 12:21
  • 1
    Why should not we use "!=" instead of "if let" to check if the optional variable has a value like - if optionalName != nil { greeting = "Hello, \(name)" } – Nuibb Nov 02 '16 at 09:19
  • 4
    @Nuibb because when using `if let` we bind the value to a non-optional variable (`name` in this example). Your example wouldn't compile because now there is no variable called `name`. If you changed your example to use `optionalName` it would print out as `Hello, Optional("John Appleseed")`. You could use forced unwrapping after checking against nil `Hello, \(optionalName!)` but this is just more error prone if you move that section of code somewhere without a check. – drewag Nov 02 '16 at 18:32
46

An optional is either set or not-set (not nil or nil)...leaving us with an important decision. "How should we write our code so that it can work correctly for 2 both states?". The way we unwrap the optional is what decides that for us.

There are several approaches that you can use to counter a not-set optional.

  • Crash!
  • Default the value to something — if it was not-set.
  • Gracefully fail ie do nothing, but also if the value was set, then assign it.
  • Gracefully fail ie do nothing, however if the value was set...do something (it's just more than a single assignment).

Below are the 4 approaches


Using forced unwrapping will crash if you don't have a value. You would want to do this if having that value is of vital importance e.g. the title of a movie (every movie MUST have a name) . ! is used for forced unwrapping.

movieTitle = movie.title!

Using nil coalescing is another way that will give you more control, meaning it won't crash if the value isn't set, nor it would 'not set it nothing' if it's not set...it would do what you tell it to do e.g. it would default/set the name of movie to untitled_movie if there was no name set. ?? is used for nil coalescing.

var movieTitle = movie.title ?? "untitled_Movie"

Using optional Chaining will do nothing if you don't have a value, and will set the value if you have a value. You do this for something that having its value set is not of vital importance e.g. for the name of your actor's agent.? is used for optional chaining.

let agent = movie.leadActor?.agent //would not crash if you don't have a lead actor (optional chaining)
let agent = movie.leadActor!.agent //would crash if you don't have a lead Actor (forced wrapping)  

Using if-let ( or guard which are two different types of optional binding) will give you more control, it won't crash if the value isn't set. If the value is set, then you can do something. If it's not set then you can add an else statement.

if let supportingActor = movie.supportingActor{
print(" The supporting actor is \(supportingActor)}

This is the most commonly used way of unwrapping, as forced unwrapping is somewhat discouraged. For more discussion on why it is discouraged see here. For a good comparison between guard and if-let see guard vs. if-let


Side note:

Optional binding and optional chaining are commonly used together:

if let agent = movie.leadActor?.agent {
ContactInfo = agent.phoneNumber
} // if-let is the optional *binding* part, the movie dot leadActor dot is the optional *chaining*
 
Community
  • 1
  • 1
mfaani
  • 33,269
  • 19
  • 164
  • 293
  • Why would forced unwrapping be discouraged given a situation where movieTitle can never be anything other than a string, and all strings are valid for movieTitle? (and I don't want "untitled movie" I want "") Forced unwrapping is the only right way for this situation, it would be great if you can remove the part that says "forced unwrapping is somewhat discouraged" as that's false info. – Andy Aug 07 '19 at 15:20
  • Suppose you make a network call and the some developer of the server team made a bad decision and forgot to send the movies title. Do you want your app to crash in production? Or just write unknown title? As a matter of fact some movies on IMDb don’t have titles :). Additionally force unwrap implies that you haven’t done any logging or asserts. That’s bad. Because you won’t know know what the root cause was. – mfaani Aug 07 '19 at 15:23
  • That's a straw man argument, I never said "always use forced unwrapping." Just because you shouldn't use forced unwrapping in your example doesn't mean forced unwrapping is discouraged at every example. I gave you a scenario where forced unwrapping is the only right solution out of the four you presented. Can you provide a better solution to the scenario stated my previous comment? If not, please consider modifying your comment about "forced unwrapping is somewhat discouraged" because it's not somewhat discouraged without considering the context. – Andy Aug 07 '19 at 19:27
  • If you’re defaulting something to “” then it’s no longer an optional – mfaani Aug 07 '19 at 19:36
  • For example, that "" value is coming from text property of a UILabel instance created in storyboard, it's an optional because it can be nil if you create it dynamically, but here you are not creating it dynamically so it will always contain a string value. Are you going to not use forced unwrapping in this case, instead, you would unwrap with if-let and provide a value that is identical to its default value ""? You can, but that's pointless, unnecessary, and wordy. And what if you're using a HTTP library that will always return a dictionary even when it's an error. It's context-dependent. – Andy Aug 07 '19 at 20:04
  • as a matter of fact "" is '\0' and also not nil, which is why there is String.isEmpty that should be true for "". – Ol Sen May 22 '22 at 15:22
6

The if syntax accept 2 different conditions. The second, an optional binding, is not a boolean. This is confusing, as you can write:

if let name = optionalName {

but not

if (let name = optionalName) {

Apple documentation (Swift reference):

The value of the condition must be of type Bool or a type bridged to Bool. The condition can also be an optional binding declaration, as discussed in Optional Binding.

mfaani
  • 33,269
  • 19
  • 164
  • 293
Pierre Marty
  • 166
  • 1
  • 7
3

if only takes boolean expressions, other than that it would throw an error, so this code snippet saying

if let name = optionalName {

}else{

}

if optionalName is nil then the condition is false and else statement will execute. However if optionalName has some value then the optional value is unwrapped/assigned into the constant variable ie name.

fs_tigre
  • 10,650
  • 13
  • 73
  • 146
Anurag Bhakuni
  • 2,379
  • 26
  • 32
2

Whenever you are working with weak references, optional types it would be better to use if let to safe guard your code and avoid crashes Here is the examples

var middleName :String? = "some thing"
if let isExistsMiddleName = middleName {
// do some thing here
} else {
// no middle name
}
Narendra G
  • 491
  • 4
  • 9
-3

ABAP has a nicer implementation of this In ABAP: if optionalName is not initial. //print greeting with name