Swift `if let` Shorthand

Jul 26, 2022#swift#swift-5.7

Before Swift 5.7, to create an unwrapped variable that shadows an existing optional variable, you have to repeat the referenced identifier twice, which can cause these optional binding conditions to be verbose, especially when using lengthy variable names.

let numberOfDaysYouLearntSwift: Int? = 100
let numberOfDaysYouLearntJavaScript: Int? = 200

if let numberOfDaysYouLearntSwift = numberOfDaysYouLearntSwift,
  let numberOfDaysYouLearntJavaScript = numberOfDaysYouLearntJavaScript {
  /* ... */
}

One thing to remember here is that you want to shadow the existing optional variable, you can come up with different short names. This approach, however, reduces clarity at the point of use for the unwrapped variables. Instead of encouraging short variable names, we should allow for the ergonomic use of descriptive variable names.

let numberOfDaysYouLearntSwift: Int? = 100
let numberOfDaysYouLearntJavaScript: Int? = 200

if let swiftDays = numberOfDaysYouLearntSwift,
  let jsDays = numberOfDaysYouLearntJavaScript {
  /* ... */
}

Swift 5.7 introduces if let shorthand syntax that allows you to omit the right-hand expression, and allow the compiler to automatically shadow the existing variable with that name.

let numberOfDaysYouLearntSwift: Int? = 100
let numberOfDaysYouLearntJavaScript: Int? = 200

if let numberOfDaysYouLearntSwift, let numberOfDaysYouLearntJavaScript {
  /* ... */
}

This is a fairly natural extension to the existing syntax for optional binding conditions, less verbose, and noticeably easier to read and write. You can use this syntax in all conditional control flow statements:

if let foo { /* ... */ }
if var foo { /* ... */ }

else if let foo { /* ... */ }
else if var foo { /* ... */ }

guard let foo else { /* ... */ }
guard var foo else { /* ... */ }

while let foo { /* ... */ }
while var foo { /* ... */ }

This syntax following the introducer serves as both an evaluated expression and an identifier for the newly-defined non-optional variable. You can’t use shorthand unwrapping for members nested in other objects.

if let foo.bar { // error: unwrap condition requires a valid identifier
  /* ... */
}

Like with existing optional bindings, this new syntax would support implifict self references to unwrap optional members of self. For example, the usage in this example would be permitted:

class Foo {
  let bar: String?
  func something() {
    if let bar { // unwraps `self.bar`.
      /* ... */
    }
  }
}