171

I'm unwrapping two values from a dictionary and before using them I have to cast them and test for the right type. This is what I came up with:

var latitude : AnyObject! = imageDictionary["latitude"]
var longitude : AnyObject! = imageDictionary["longitude"]

if let latitudeDouble = latitude as? Double  {
   if let longitudeDouble = longitude as? Double {
       // do stuff here
   }
}

But I would like to pack the two if let queries into one. So that it would something like that:

if let latitudeDouble = latitude as? Double, longitudeDouble = longitude as? Double {
    // do stuff here
}

That syntax is not working, so I was wondering if there was a beautiful way to do that.

mfaani
  • 33,269
  • 19
  • 164
  • 293
suntoch
  • 1,834
  • 2
  • 13
  • 13
  • 1
    possible duplicate of [unwrapping multiple optionals in if statement](http://stackoverflow.com/questions/24548999/unwrapping-multiple-optionals-in-if-statement) – Jack Jul 06 '14 at 01:55
  • There may be a way to use a switch statement to pattern match the types. Take a look at the [Docs](https://developer.apple.com/library/prerelease/ios/documentation/swift/conceptual/swift_programming_language/TypeCasting.html#//apple_ref/doc/uid/TP40014097-CH22-XID_448): – lomokat Jul 06 '14 at 02:23
  • 1
    possible duplicate of [Using "if let..." with many expressions](http://stackoverflow.com/questions/24118900/using-if-let-with-many-expressions) – rickster Feb 09 '15 at 20:51

3 Answers3

346

Update for Swift 3:

The following will work in Swift 3:

if let latitudeDouble = latitude as? Double, let longitudeDouble = longitude as? Double {
    // latitudeDouble and longitudeDouble are non-optional in here
}

Just be sure to remember that if one of the attempted optional bindings fail, the code inside the if-let block won't be executed.

Note: the clauses don't all have to be 'let' clauses, you can have any series of boolean checks separated by commas.

For example:

if let latitudeDouble = latitude as? Double, importantThing == true {
    // latitudeDouble is non-optional in here and importantThing is true
}

Swift 1.2:

Apple may have read your question, because your hoped-for code compiles properly in Swift 1.2 (in beta today):

if let latitudeDouble = latitude as? Double, longitudeDouble = longitude as? Double {
    // do stuff here
}

Swift 1.1 and earlier:

Here's the good news - you can totally do this. A switch statement on a tuple of your two values can use pattern-matching to cast both of them to Double at the same time:

var latitude: Any! = imageDictionary["latitude"]
var longitude: Any! = imageDictionary["longitude"]

switch (latitude, longitude) {
case let (lat as Double, long as Double):
    println("lat: \(lat), long: \(long)")
default:
    println("Couldn't understand latitude or longitude as Double")
}

Update: This version of the code now works properly.

Confused Vorlon
  • 9,659
  • 3
  • 46
  • 49
Nate Cook
  • 92,417
  • 32
  • 217
  • 178
  • it works for me in 6.1.1, @AaronBratcher why not to you? – Daniel Gomez Rico Jan 19 '15 at 19:11
  • 1
    In Swift 1.2, it's now possible to do this in one line: http://stackoverflow.com/a/28418847/1698576 – smithclay Feb 09 '15 at 20:37
  • In your code, you have 2 optionals being unwrapped. Is it to be used always like that? I have different confusing code. `if let app = UIApplication.sharedApplication().delegate as? AppDelegate, let window = app.window {...}`. Is the *2nd* `let` also optional binding? I mean `app` is no longer an optional. right? – mfaani Nov 28 '16 at 17:50
  • 1
    It is. `app` is no longer an optional, but its `window` property is (its type is `UIWindow?`), so that's what you're unwrapping. – Nate Cook Nov 28 '16 at 18:11
13

With Swift 3, you can use optional chaining, switch statement or optional pattern in order to solve your problem.


1. Using if let (optional binding / optional chaining)

The Swift Programming Language states about optional chaining:

Multiple queries can be chained together, and the entire chain fails gracefully if any link in the chain is nil.

Therefore, in the simplest case, you can use the following pattern to use multiple queries in your optional chaining operation:

let dict = ["latitude": 2.0 as AnyObject?, "longitude": 10.0 as AnyObject?]
let latitude = dict["latitude"]
let longitude = dict["longitude"]

if let latitude = latitude as? Double, let longitude = longitude as? Double {
    print(latitude, longitude)
}

// prints: 2.0 10.0

2. Using tuples and value binding in a switch statement

As an alternative to a simple optional chaining, switch statement can offer a fine grained solution when used with tuples and value binding:

let dict = ["latitude": 2.0 as AnyObject?, "longitude": 10.0 as AnyObject?]
let latitude = dict["latitude"]
let longitude = dict["longitude"]

switch (latitude, longitude) {
case let (Optional.some(latitude as Double), Optional.some(longitude as Double)):
    print(latitude, longitude)
default:
    break
}

// prints: 2.0 10.0
let dict = ["latitude": 2.0 as AnyObject?, "longitude": 10.0 as AnyObject?]
let latitude = dict["latitude"]
let longitude = dict["longitude"]

switch (latitude, longitude) {
case let (latitude as Double, longitude as Double):
    print(latitude, longitude)
default:
    break
}

// prints: 2.0 10.0
let dict = ["latitude": 2.0 as AnyObject?, "longitude": 10.0 as AnyObject?]
let latitude = dict["latitude"]
let longitude = dict["longitude"]

switch (latitude as? Double, longitude as? Double) {
case let (.some(latitude), .some(longitude)):
    print(latitude, longitude)
default:
    break
}

// prints: 2.0 10.0
let dict = ["latitude": 2.0 as AnyObject?, "longitude": 10.0 as AnyObject?]
let latitude = dict["latitude"]
let longitude = dict["longitude"]

switch (latitude as? Double, longitude as? Double) {
case let (latitude?, longitude?):
    print(latitude, longitude)
default:
    break
}

// prints: 2.0 10.0

3. Using tuples with if case (optional pattern)

if case (optional pattern) provides a convenient way to unwrapped the values of optional enumeration. You can use it with tuples in order to perform some optional chaining with multiple queries:

let dict = ["latitude": 2.0 as AnyObject?, "longitude": 10.0 as AnyObject?]
let latitude = dict["latitude"]
let longitude = dict["longitude"]

if case let (.some(latitude as Double), .some(longitude as Double)) = (latitude, longitude) {
    print(latitude, longitude)
}

// prints: 2.0 10.0
let dict = ["latitude": 2.0 as AnyObject?, "longitude": 10.0 as AnyObject?]
let latitude = dict["latitude"]
let longitude = dict["longitude"]

if case let (latitude as Double, longitude as Double) = (latitude, longitude) {
    print(latitude, longitude)
}

// prints: 2.0 10.0
let dict = ["latitude": 2.0 as AnyObject?, "longitude": 10.0 as AnyObject?]
let latitude = dict["latitude"]
let longitude = dict["longitude"]

if case let (.some(latitude), .some(longitude)) = (latitude as? Double, longitude as? Double) {
    print(latitude, longitude)
}

// prints: 2.0 10.0
let dict = ["latitude": 2.0 as AnyObject?, "longitude": 10.0 as AnyObject?]
let latitude = dict["latitude"]
let longitude = dict["longitude"]

if case let (latitude?, longitude?) = (latitude as? Double, longitude as? Double) {
    print(latitude, longitude)
}

// prints: 2.0 10.0
Imanou Petit
  • 89,880
  • 29
  • 256
  • 218
6

Swift 3.0

if let latitudeDouble = latitude as? Double, let longitudeDouble = longitude as? Double {
    // do stuff here
}
norbDEV
  • 4,795
  • 2
  • 37
  • 28