Swift: testing against optional value in switch case

46,007

Solution 1

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")        
}

Solution 2

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.
}

Solution 3

In Swift 4 you can use Optional : ExpressibleByNilLiteral of Apple to wrappe optional

https://developer.apple.com/documentation/swift/optional

Example

enum MyEnum {
    case normal
    case cool
}

some

let myOptional: MyEnum? = MyEnum.normal

switch smyOptional {
    case .some(.normal): 
    // Found .normal enum
    break

    case .none: 
    break

    default:
    break
}

none

let myOptional: MyEnum? = nil

switch smyOptional {
    case .some(.normal): 
    break

    case .none: 
    // Found nil
    break

    default:
    break
}

default

let myOptional: MyEnum? = MyEnum.cool

switch smyOptional {
    case .some(.normal): 
    break

    case .none: 
    break

    default:
    // Found .Cool enum
    break
}

Enum with value

enum MyEnum {
    case normal(myValue: String)
    case cool
}

some value

let myOptional: MyEnum? = MyEnum.normal("BlaBla")

switch smyOptional {
case .some(.normal(let myValue)) where myValue == "BlaBla":
    // Here because where find in my myValue "BlaBla"
    break

// Example for get value
case .some(.normal(let myValue)):
    break

// Example for just know if is normal case enum
case .some(.normal):
    break

case .none:
    break

default:

    break
}

Solution 4

You can explicitly mention all cases along with nil as an additional case to handle the optional:

switch optionalEnumValue {
case .caseOne:
    break
case .caseTwo:
    break
case .caseN:
    break
case nil:
    break
}
Share:
46,007

Related videos on Youtube

George WS
Author by

George WS

Coding in class, studying SICP over breaks, and working on iOS apps whenever I can.

Updated on October 29, 2021

Comments

  • George WS
    George WS over 2 years

    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).

  • Martin R
    Martin R over 7 years
    The question is about matching a non-optional value against an optional, this answer is the other way around.
  • Slipp D. Thompson
    Slipp D. Thompson over 7 years
    True, however this answer was originally written by the OP as an update to the question so to him it was irrefutably a viable solution; I just moved it to a community wiki answer. Perhaps @GeorgeWS can clarify as to why switching the switch & case args works for his use-case?
  • mfaani
    mfaani over 6 years
    I'm a bit lost. what's the difference between your first two cases? someValue? is some other defined value, but case let val? is just the safe unwrapped version of someOptional?!
  • Slipp D. Thompson
    Slipp D. Thompson over 6 years
    @Honey It's not a real-world code example; it's simply a variation on rintaro's answer. So go ask him/her that question— my answer is functionally equivalent to the code in his/hers. If you were to ask rintaro though, I believe the answer would be 1. it mirrors what's in the linked Apple docs; 2. it only demonstrates the syntax; it does not accomplish a distinct calculation or business logic goal.
  • Slipp D. Thompson
    Slipp D. Thompson over 6 years
    @Honey Also, rintaro's answer was originally written for Swift 1.x and updated for Swift 2. It's possible that the version without let no longer compiles. I can't remember right now why that would've worked back in the day.
  • George WS
    George WS over 4 years
    @Honey I think you're exactly right about the first two cases—the difference is that someValue was presumed to be a specific, known value we were checking for, whereas val was just any other non-optional value that someOptional might contain. I updated the code sample to make this more clear (renamed someValueknownValue and valotherValue, showed knownValue being assigned before the switch, and added more precise comments). Thanks for pointing out the confusing-ness of this—*totally* see why it was confusing, looking back at it a year+ later! 😂
  • George WS
    George WS over 4 years
    @SlippD.Thompson The short answer to why this worked for me is… I don't remember. 🤷🏼‍♂️ Longer answer, looking back—I think switching the switch and cases worked here for me because I was able to find out what I wanted to know with either method of constructing the switch statement. Looking at the original question, I wanted to know if… // someOptional is non-nil, and someValue equals the unwrapped contents of someOptional …which is true both in the first case of my imagined-switch in the question, and in the analogous second case here (case let otherValue?).
  • pkamb
    pkamb over 3 years
    You mention ExpressibleByNilLiteral but then that is not actually used in the enum declarations etc. What is that?