6

When using optional binding to unwrap a single method call (or optional chaining for a long method call chain), the syntax is clear and understandable:

if let childTitle = theItem.getChildItem()?.getTitle() {
    ...
}

But, when provided the variable as a parameter, I find myself either using:

func someFunction(childTitle: String?) {
    if let theChildTitle = childTitle {
        ...
    }
}

or even just redefining it with the same name:

if let childTitle = childTitle { ... }

And I've started wondering if there is a shortcut or more efficient of performing a nil check for the sole purpose of using an existing variable. I've imagined something like:

if let childTitle { ... }

Does something like this exist, or at least an alternative to my above two interim solutions?

Craig Otis
  • 31,257
  • 32
  • 136
  • 234

3 Answers3

16

Edit/update: Xcode 14 • Swift 5.7

swift-evolution proposal SE-0345 if let shorthand for shadowing an existing optional variable:

if let childTitle {

}

guard let childTitle else {
    return
}

Original answer

No. You should unwrap your optionals just redefining it with the same name as you mentioned. This way you don't need to create a second var.

func someFunction(childTitle: String?) {
    if let childTitle = childTitle {
        ...
    }
}

update: Xcode 7.1.1 • Swift 2.1

You can also use guard as follow:

func someFunction(childTitle: String?) {
    guard let childTitle = childTitle else {
        return
    }

    // childTitle it is not nil after the guard statement
    print(childTitle)
}
Leo Dabus
  • 229,809
  • 59
  • 489
  • 571
  • I'm aware of this, and mention it in my question - however, I'm wondering if there is `"... at least an alternative to my above two interim solutions?"` – Craig Otis Feb 25 '15 at 00:27
  • @craig sorry did not notice that BTW thats the recommended approach. forget about the other ones – Leo Dabus Feb 25 '15 at 00:28
  • 1
    I don't see any other solution. You could just use your parameter as an optional but if the code you're going to write requires a non-nilable value, you must use optional binding. Hopefully, Apple will improve this situation in Swift 1.2+ – Skoua Feb 25 '15 at 00:39
  • 4
    Interesting that you can reuse the same name. Apparently this is called [variable shadowing](https://stackoverflow.com/a/32250151). – James Toomey Jan 19 '18 at 23:35
  • 3
    I wish Swift had a special keyword to make that last code block less ugly, like... guard unwrap childTitle else{ – TealShift Nov 19 '18 at 20:47
  • @TealShift check my last edit – Leo Dabus Mar 05 '23 at 01:24
  • 1
    @JamesToomey check my last edit – Leo Dabus Mar 05 '23 at 01:26
  • 1
    @LeoDabus Love that they added this syntax! – James Toomey Mar 08 '23 at 03:17
  • @JamesToomey me too. Looks much better – Leo Dabus Mar 08 '23 at 15:22
  • 1
    @JamesToomey Swift 5.8 has already implemented [SE-0365](https://github.com/apple/swift-evolution/blob/main/proposals/0365-implicit-self-weak-capture.md) as well which is really welcome – Leo Dabus Mar 08 '23 at 15:27
0

Here's the only alternative I'm aware of.

func someFunction(childTitle: String?) {
    if childTitle != nil {
        ...
    }
}

But then childTitle is still an optional, so you would have to unwrap it every time you use it: childTitle!.doSomething(). It shouldn't affect performance, though.

sangonz
  • 589
  • 2
  • 8
-1

I've gotten into the habit of just reassigning to the same name. That eliminates the need to come up with a separate name and avoids any confusion about whether the new name is supposed to be used anywhere. However, note the output after the block:

var test: String? = "123"
if var test = test {
    test += "4"
    test += "5"
}
print(test) // prints "123"

If you need to access the modified value after the block, you can assign the variable to a new name and assign the new name back to the old name inside the block:

var test: String? = "123"
if var testWorking = test {
    testWorking += "4"
    testWorking += "5"
    test = testWorking
}
print(test) // prints "12345"
arlomedia
  • 8,534
  • 5
  • 60
  • 108