LeonS
LeonS

Reputation: 2714

unwrapping multiple optionals in if statement

I want to unwrap two optionals in one if statement, but the compiler complaints about an expected expression after operator at the password constant. What could be the reason?

    if let email = self.emailField?.text && let password = self.passwordField?.text
    {
        //do smthg
    }

Done in Swift.

Upvotes: 67

Views: 32925

Answers (8)

smithclay
smithclay

Reputation: 3386

Great news. Unwrapping multiple optionals in a single line is now supported in Swift 1.2 (XCode 6.3 beta, released 2/9/15).

No more tuple/switch pattern matching needed. It's actually very close to your original suggested syntax (thanks for listening, Apple!)

if let email = emailField?.text, let password = passwordField?.text {

}

Another nice thing is you can also add where for a "guarding condition":

var email: String? = "[email protected]"
var name: String? = "foo"

if let n = name, let e = email where contains(e, "@") {
  println("name and email exist, email has @")
}

Reference: XCode 6.3 Beta Release Notes

Upvotes: 138

Wasim
Wasim

Reputation: 1140

Swift 4

if let suggestions = suggestions, let suggestions1 = suggestions1 {

        XCTAssert((suggestions.count > suggestions1.count), "TEST CASE FAILED: suggestion is nil. delete sucessful");
}

Upvotes: 4

Henningsson
Henningsson

Reputation: 1315

Update for Swift 3:

if let email = emailField?.text, let password = passwordField?.text {
}

each variable must now be preceded by a let keyword

Upvotes: 47

Tom Lokhorst
Tom Lokhorst

Reputation: 13768

Before Swift 1.2

Like @James, I've also created an unwrap function, but this one uses the existing if let for control flow, instead of using a closure:

func unwrap<T1, T2>(optional1: T1?, optional2: T2?) -> (T1, T2)? {
  switch (optional1, optional2) {
  case let (.Some(value1), .Some(value2)):
    return (value1, value2)
  default:
    return nil
  }
}

This can be used like so:

if let (email, password) = unwrap(self.emailField?.text, self.passwordField?.text)
{
  // do something
}

From: https://gist.github.com/tomlokhorst/f9a826bf24d16cb5f6a3

Note that if you want to handle more cases (like when one of the two fields is nil), you're better off with a switch statement.

Upvotes: 5

James Tang
James Tang

Reputation: 995

Based on @Joel's answer, I've created a helper method.

func unwrap<T, U>(a:T?, b:U?, handler:((T, U) -> ())?) -> Bool {
    switch (a, b) {
    case let (.Some(a), .Some(b)):
        if handler != nil {
            handler!(a, b)
        }
        return true
    default:
        return false
    }
}

// Usage

unwrap(a, b) {
    println("\($0), \($1)")
}

Upvotes: 0

Joel Edstr&#246;m
Joel Edstr&#246;m

Reputation: 271

How about wrapping the optionals in a tuple and using switch to pattern match?

switch (self.emailField?.text, self.passwordField?.text) {
case let (.Some(email), .Some(password)):
    // unwrapped 'email' and 'password' strings available here
default:
    break
}

It's definitely a bit noisier, but at least it could also be combined with a where clause as well.

Upvotes: 26

Grimxn
Grimxn

Reputation: 22487

The usage

if let x = y {
}

is not equivalent to

if (let x = y) { // this is actually not allowed
}

"if let" is effectively a two-word keyword, which is equivalent to

if y != nil {
    let x = y!
    // rest of if let block
}

Upvotes: 9

Ashley Mills
Ashley Mills

Reputation: 53101

I can't explain why the above code doesn't work, but this would be good a replacement:

if let email = self.emailField?.text 
{
    if let password = self.passwordField?.text 
    {
        //do smthg
    }
}

Upvotes: 1

Related Questions