unwrapping multiple optionals in if statement
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, password = passwordField?.text {
}
Another nice thing is you can also add where
for a "guarding condition":
var email: String? = "baz@bar.com"
var name: String? = "foo"
if let n = name, e = email where contains(e, "@") {
println("name and email exist, email has @")
}
Reference: XCode 6.3 Beta Release Notes
Swift unwrap multiple optionals depending on each other in one if statement
If you only wish to work with the (possible) instance inner variable b
(type B
) of a
(type A
), you could use optional chaining in a single optional binding clause
class A {
var optionalB : B?
}
class B {}
var optionalA : A?
// in case you needn't actually use optionalA, but only its
// possibly non-nil instance member `optionalB`
if let b = optionalA?.optionalB {
//do something with b
}
Unwrapping and checking multiple optionals on single line
If you don't need v
and s
inside of the body of the if
, you can just do the comparison directly:
if jsonResult["valid"] as? Int == 1 && jsonResult["status"] as? String == "ok" {
// Do something
}
Swift unwrapping for multiple optionals
If you're attempting to print the non-nil
values as a comma-separated list, then I think @MartinR's suggestion of using flatMap()
is the best:
let one: String?
let two: String?
let three: String?
one = "one"
two = nil
three = "three"
let nonNils = [one, two, three].flatMap { $0 }
if !nonNils.isEmpty {
print(nonNils.joinWithSeparator(","))
}
Output:
one,three
Using multiple let-as within a if-statement in Swift
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.
Unwrapping multiple Swift optionals
While Leonardo's answer is a good one, it can lead to exceptions unless you know the objects are non-optional, a better pattern is this, which should be regarded as pseudocode:
if let frame = NSScreen.screens()?.first?.frame {
// Do something with frame.
}
In other words, use optional chaining (?.
), but only use let
with the very last part of the chain.
You could also create an operator like this if you want to chain optionals that are of a different type, in which only the value of the last one will be returned:
infix operator ??? { associativity left }
func ??? <T1, T2>(t1: T1?, t2: @autoclosure () -> T2?) -> T2? {
return t1 == nil ? nil : t2()
}
if let frame = self.window ??? NSScreen.screens()?.first?.frame {
// Do something with frame
}
This is, of course, inspired by Haskell's bind. But as fun as this is, I don't recommend it. I think it's clear as mud. (By the way, the difference between ???
and ??
is that the former does not require the lhs and rhs to be of the same type (or supertype), while the latter does. ???
always returns its rhs or nil
.)
Using if let... with many expressions
Update for Swift 1.2
Since Swift 1.2, if let
allows unwrapping multiple optionals, so you can now just write this, as in your example:
if let x = someDict[someKey], y = someDict[someOtherKey] { … }
You can even interleave conditions such as:
if let x = someDict[someKey] where x == "value", y = someDict[someOtherKey] { … }
This used to be valid before Swift 1.2
Here's how you would do it without an ugly force-upwrapping:
switch (dict["a"], dict["b"]) {
case let (.Some(a), .Some(b)):
println("match")
default:
println("no match")
}
Still pretty verbose, actually.
This works because an optional type of the form Type?
is actually shorthand for Optional<Type>
, which is an enum that looks roughly like this:
enum Optional<T> {
case None
case Some(T)
}
You can then use pattern matching as for any other enum.
Edit: I've seen people write helper functions like this one (sorry for the lack of attribution, I don't remember where I saw it):
func unwrap<A, B>(a: A?, b: B?) -> (A, B)? {
switch (a, b) {
case let (.Some(a), .Some(b)):
return (a, b)
default:
return nil
}
}
Then you can keep using the if let
construct, namely like this:
if let (a, b) = unwrap(dict["a"], dict["b"]) {
println("match: \(a), \(b)")
} else {
println("no match")
}
Can you safely unwrap nested optionals in swift in one line?
You could do it like so:
if let title = view.annotation?.title as? String {
}
view.annotation?.title
is a double optional string: String??
since both the property annotation
of an MKAnnotationView
, and its own property title
, are optional.
You could also use the guard statement like so:
guard let title = view.annotation?.title as? String else {
return
}
//use title in the rest of the scope
you could also use a switch
statement :
switch title {
case .some(.some(let t)):
//use the title here
print(t)
default:
break
}
Related Topics
Flatten an Array of Arrays in Swift
How to Compare Enum With Associated Values by Ignoring Its Associated Value in Swift
Real Time Nstask Output to Nstextview With Swift
What Does It Mean That String and Character Comparisons in Swift Are Not Locale-Sensitive
Nsurlsession Concurrent Requests With Alamofire
Photopicker Discovery Error: Error Domain=Pluginkit Code=13
Object X of Class Y Does Not Implement Methodsignatureforselector in Swift
How to Store 1.66 in Nsdecimalnumber
Setting Device Orientation in Swift Ios
Share Data Between Main App and Widget in Swiftui For iOS 14
String Value to Unsafepointer≪Uint8≫ Function Parameter Behavior
Xcode 8 Beta 3 Use Legacy Swift Issue
Swift Constants: Struct or Enum
Using Decodable in Swift 4 With Inheritance
Swiftui Picker Separate Texts For Selected Item and Selection View