Optional Binding and the if let usage

Hello All! It's my first post here and I'm a beginner when it comes to Swift (and my previous programming experience is limited to 1+ year with Java).

I'm learning using tremendously great set of books from Paul Hudson, boosted by official docs and other sites. I'm now expanding my understanding of Optionals (I understand nil's presence, why it needs unwrapping etc.). For the reference, I would like to kindly ask you to visit this article:

https://www.hackingwithswift.com/read/0/12/optionals

I have several questions related to if let and the optional binding.

  1. Using the example from the article:

    if let haterStatus = getHaterStatus(weather: "rainy") {
    takeHaterAction(status: haterStatus)
    }

Can we resolve this if let without specifying the weather in the first brackets? Function getHaterStatus returns nil only for "sunny" weather.

  1. Can you elaborate on what's happening in the if let above step by step? My understanding:
  • if let checks if there's nil

  • we define haterStatus

  • we use getHaterStatus to get hater's status for rainy weather (it will return "Hate")

  • at this point haterStatus = "Hate"

  • takeHaterAction function is now used and as it uses "Hate" as a status, it prints "Hating"

Is that correct? It's hard example because I've started wondering why we haven't checked the behavior for sunny weather in getHaterStatus.

Why don't we deal with nil status in this if let?

  1. Is it possible to have if let inside the getHaterStatus function?

  2. For the next example with yearAlbumRelease function, what's the expected solution to if let? Here's my approach, but - similar to question one - I'm not sure if we cannot write a more general if let:

    if let exampleYear = yearAlbumReleased(name: "Pawel") {
    print(exampleYear)
    } else {
    print("No year specified for this artist!")
    }

Thanks in advance for all the help you can share!

Hi, welcome to the Swift Forums!
Let me try to answer your questions:

I'm not sure what you mean by "resolve this if let", but regarding not specifying the weather in brackets you can't. This is because your getHaterStatus function requires a string to be passed in so if you don't pass it a value it doesn't know what other String to choose instead. You can change the function like so:

func getHaterStatus(weather: String = "sunny") -> String? {
    ...
}

which lets Swift know you can leave out the weather parameter when calling this function and it will choose "sunny" as a default.
If you mean that because you are calling getHaterStatus(weather:) with a hard-coded value of "rainy" it will never return nil, you're right, but imagine that, as a more real life example, instead of "rainy" you have a variable containing a string you got from a user of your program or from a weather library. Now you cannot assume getHaterStatus will never return nil since the user might have have said it is a "sunny" day today.

What is happening is:

  1. getHaterStatus(weather:) gets called with the string "rainy" and it will return Optional<String>.some("Hate")
  2. if let will check if the returned optional contains a value or not, in this case it does: "Hate"
  3. if let will unwrap the optional (get the value from within the optional) and assign it to haterStatus
  4. if let will then run the code inside the curly braces using the newly assigned haterStatus constant
  5. if in step 2. if let saw that the function returned nil (same as returning Optional<String>.none explicitly) it will not do steps 3. and 4. and will run the else block if it exists (it does not in this case)

Keep in mind that this is just an example, it might not make sense in this case, but there are cases where you might be interested in only running certain code if a value actually exists in the first place, and if it doesn't exist, not running any code at all might actually be what you want/the correct thing to do.

You can use if let something = somethingElse any time somethingElse is of an Optional<Something> type where Something is any type you want like Optional itself or String. With this in mind, to use if let inside getHaterStatus you would have to accept an optional string as the weather parameter. This could also make sense if for example the user of your code does not know the current weather or if there is no internet connection and the weather library you are using passed nil as a parameter to your function. However, you don't actually need weather to be optional though, you only need somethingElse to be optional so you might do some operation before which might fail and thus give you nil.

If you mean what code will run in this case, it will be the one in the else block: print("No year specified for this artist!"), since the implementation of yearAlbumReleased does not check for "Pawel" and so all of the if checks inside will fail and the code will run until the return nil.

If I misunderstood any of the questions please tell me which and if you can rephrase it another way I would appreciate it.
I hope this helps!

1 Like

Happy to be here, thanks! :slight_smile:

I'm not sure what you mean by "resolve this if let", but regarding not specifying the weather in brackets you can't. This is because your getHaterStatus function requires a string to be passed in so if you don't pass it a value it doesn't know what other String to choose instead.

Sorry for not being precise with wording. I'm having problem with understanding the logic behind if let and any/specific String. Nil is being returned only for "sunny", but any other String will return String as well. This is where I'm confused. If, for example, I want user to fill any String (and I assume he/she fills anything except "sunny") and I use this if let to check if it's not nil, why do we specify the weather? In other words, is it possible to make if let similar to universal func, where XYZ in the code below is specified by the user?

if let haterStatus = getHaterStatus(weather: "XYZ") {
takeHaterAction(status: haterStatus)
}

If you mean that because you are calling getHaterStatus(weather:) with a hard-coded value of "rainy" it will never return nil , you're right, but imagine that, as a more real life example, instead of "rainy" you have a variable containing a string you got from a user of your program or from a weather library. Now you cannot assume getHaterStatus will never return nil since the user might have have said it is a "sunny" day today.

I think that's my biggest problem right now is seeing "if let" outside of a function and having some data hard-coded. Using the example from the article, I would be able to use if let without specifying a String inside its code. What I think my mind suggests is to have universal code for if let, and have the weather as a separate variable.

The other question is, having the code as in the example article, what happens if the weather is neither "sunny" (returning nil from func getHaterStatus) nor "rainy" (as specified in if let). I know I can write something like this:

var currentMoodTowardsWeather = getHaterStatus(weather: "gloomy")

and that it will return "Hate", and if I write:

var currentMoodTowardsWeather2 = getHaterStatus(weather: "sunny")

I will get nil. But then, what was the reasoning behind if let being outside of the function? I've read numerous articles on if let and I'm trying to put my thoughts in order and fit everything together.

With this in mind, to use if let inside getHaterStatus you would have to accept an optional string as the weather parameter. This could also make sense if for example the user of your code does not know the current weather or if there is no internet connection and the weather library you are using passed nil as a parameter to your function. However, you don't actually need weather to be optional though, you only need somethingElse to be optional so you might do some operation before which might fail and thus give you nil .

May I ask you to modify the example code from the article to reflect that?

If you mean what code will run in this case, it will be the one in the else block: print("No year specified for this artist!") , since the implementation of yearAlbumReleased does not check for "Pawel" and so all of the if checks inside will fail and the code will run until the return nil .

The example code is as follows:

func yearAlbumReleased(name: String) -> Int? {
    if name == "Taylor Swift" { return 2006 }
    if name == "Fearless" { return 2008 }
    if name == "Speak Now" { return 2010 }
    if name == "Red" { return 2012 }
    if name == "1989" { return 2014 }

    return nil
}

How should I unwrap the result using if let to check whether a value exists or not?

I think it might be easier to remove functions out of the equation. You can more or less do this.

let status: String? = "Hate" // status is of type `String?`

if let haterStatus = status {
  // haterStatus is of type `String`
  takeHaterAction(status: haterStatus)
}
// haterStatus does NOT exist here.

functions like getHaterStatus just facilitates the value computation. You can even do

let status = getHaterStatus(weather: "sunny")
if let haterStatus = status {
  takeHaterAction(status: haterStatus)
}

So you'll see that now you need to compute the value of status first, then do the if let.


You may notice that, in Java, everything can be null. Every. Single. Thing.
This causes a lot of problems with the infamous NullPointerException as people tend to forget to check for null.

So in Swift, we instead say, we don't allow null (we call it nil) unless stated otherwise, and Optional is that stated otherwise. String? can be a valid String instance, or nil.

Swift is also very adamant that you checked for nil whenever you want to do something. One way is via if let:

let status: String? = ... // May be valid String here. Maybe nil.

if let haterStatus = status {
  // We just checked, status is a valid String.
  // Put that valid string into `haterStatus`.
  // Since we KNOW `haterStatus` status contains a valid string, it is of type `String`, not `String?`
} else {
  // We just checked, status is nil.
  // So we don't have `haterStatus` here.
}

There's also something you can do with Optional directly (without the need to check for nil), but maybe that's for another time.

2 Likes

Thanks for the answer, @Lantua. That clarifies a lot. "Stating otherwise" also sets my mindset differently, I will keep this in mind.

To follow-up, with your thread: as far as I know, I'm not allowed to have "return" in "if let" as it's not a function. Could you give me an example of getHaterStatus and takeHaterAction combined with "if let", but with the variable "weather" existing separately and not hard-coded into function?

Close. if-let is not a function, it is a control structure, like if, while, etc.

So, like while, you can't return from if-let, but you can return from function inside if-let, like how I'll do it in the next part.

Maybe something like this?

func getHaterStatus(weather: String?) -> String? {
    if weather == "sunny" {
        return nil // no hate, so no hate status
    }
    if weather == "rainy" {
        return "hate getting wet"
    }
    if let weather = weather {
        // I dunno, it's not sunny, I just hate it.
        return "just hate \(weather)"
    }

    // weather is nil here... well
    return "hate nothingness"
}

func takeHaterAction(haterStatus: String) {
    print("I \(haterStatus)")
}

print("How's the weather today?")

/// Don't mind here, just for demo purpose
var weather = readLine()
if weather?.isEmpty ?? false {
    weather = nil
}
/// Start minding again from here


if let haterStatus = getHaterStatus(weather: weather) {
    takeHaterAction(haterStatus: haterStatus)
} else {
    print("Oddly enough, I don't hate it.")
}

So you can use if let inside function just fine, and even return a value.
So this program would read weather value from user, then set it to nil is user just enter empty line.

So now getHaterStatus wants to handle nil case (for empty line). It can also return nil in case it doesn't hate it. The outside is largely the same.

PS

You can enclose code in ` like this: `code` => code.
You can also use triple ` for code block like this:

```
code block
```

becomes

code block
1 Like

WOW. Thank you, @Lantua. That’s well beyond what I’ve expected, really appreciated. I will use the code you’ve shared to practice the whole process.

And thanks for the hints on the text formatting, I will utilize them as well.

Terms of Service

Privacy Policy

Cookie Policy