Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift: testing against optional value in switch case

In Swift, how can I write a case in a switch statement that tests the value being switched against the contents of an optional, skipping over the case if the optional contains nil?

Here's how I imagine this might look:

let someValue = 5
let someOptional: Int? = nil

switch someValue {
case someOptional:
    // someOptional is non-nil, and someValue equals the unwrapped contents of someOptional
default:
    // either, someOptional is nil, or someOptional is non-nil but someValue does not equal the unwrapped contents of someOptional
}

If I just write it exactly like this, the compiler complains that someOptional is not unwrapped, but if I explicitly unwrap it by adding ! to the end, I of course get a runtime error any time someOptional contains nil. Adding ? instead of ! would make some sense to me (in the spirit of optional chaining, I suppose), but doesn't make the compiler error go away (i.e. doesn't actually unwrap the optional).

like image 388
George WS Avatar asked Nov 15 '14 01:11

George WS


People also ask

How can I tell if an optional value is nil Swift?

You can use if statement and compare optional with nil to find out whether a optional contains a value or not. You can use the comparison operator "equal to" operator ( == ) or the "not equal to" operator ( != ) in the if statement.

Is Break necessary in switch case Swift?

Switches in swift don't need break statement as a case statement doesn't fall through by default. You don't need to add a break to the end of all your cases.

How do you skip a switch case in Swift?

You do this by writing the break statement as the entire body of the case you want to ignore."

Can any be optional Swift?

Swift doesn't allow comparing Optional<Any> to Any, so the last line doesn't work.


2 Answers

Optional is just a enum like this:

enum Optional<T> : Reflectable, NilLiteralConvertible {
    case none
    case some(T)

    // ...
}

So you can match them as usual "Associated Values" matching patterns:

let someValue = 5
let someOptional: Int? = nil

switch someOptional {
case .some(someValue):
    println("the value is \(someValue)")
case .some(let val):
    println("the value is \(val)")
default:
    println("nil")
}

If you want match from someValue, using guard expression:

switch someValue {
case let val where val == someOptional:
    println(someValue)
default:
    break
}

And for Swift > 2.0

switch someValue {
case let val where val == someOptional:
    print("matched")
default:
    print("didn't match; default")        
}
like image 84
rintaro Avatar answered Oct 09 '22 19:10

rintaro


As of Xcode 7, “a new x? pattern can be used to pattern match against optionals as a synonym for .some(x)”. This means that in Swift 2 and later the following variation of rintaro's answer will work as well:

let knownValue = 5

switch someOptional {
case knownValue?:
    // Contents of someOptional are knownValue, defined above.
case let otherValue?:
    // Contents of someOptional are *any* non-nil value not already tested for.
    // Unwrapped contents are assigned to otherValue for use inside this case.
default:
    // someOptional is nil.
}
like image 63
5 revs, 4 users 55% Avatar answered Oct 09 '22 20:10

5 revs, 4 users 55%