Swift: testing against optional value in switch case - swift

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

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

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

In Swift 4 you can use Optional 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
}

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
}

Related

Why switch does not accept case nil in optional?

I was working with optional in switch which I noticed switch cannot accept nil as a case!
func test(value: Bool?) {
switch value {
case true: print("true")
case false: print("false")
case nil: print("nil")
}
}
Error: Switch must be exhaustive
then I used default for solving the issue, but I am looking to learn why nil cannot work in first code?
I think this is just an incorrect diagnostic. You can add a default case, and this code will work, but I'm actually surprised that works.
You see, the value you're switching on is a Bool?, but 2 of the values you're comparing it against are true and false. The typical approach for this is to use pattern matching:
func test(value: Bool?) {
switch value {
case .some(true): print("true")
case .some(false): print("false")
case .none: print("nil")
}
}
This works correctly, but it's a bit wordy. Luckily, Swift has a syntactic sugar for pattern matching against optionals, ? and nil:
func test(value: Bool?) {
switch value {
case true?: print("true")
case false?: print("false")
case nil: print("nil")
}
}
Both of these snippets compile and work fine. It does open a new question as to why your original cases worked, if only you added the default.
I suggest you post about this on Swift forums, to get more input from the compiler nerds :)
An Optional is an enum with two cases, one which has the value associated: either .some(value) or .none
The correct way to use a switch for pattern matching an optional is:
switch optional {
case .some(let value): // do your stuff with value
case .none: // do your stuff when the optional is nil
}
Alternatively you could also use map on an optional to execute a closure when its value is not nil:
optional.map { value in
// do your stuff with the value
}
With this approach the body closure passed to the map function only executes when the optional is not nil at runtime, otherwise it doesn’t executes.

switch must be exhaustive in Recursive type

enum Tree<Element: Comparable> {
case empty
indirect case node(Tree<Element>, Element, Tree<Element>)
func forEach(withLooping fn: (Element) -> Void) {
var stack = [self]
while !stack.isEmpty {
let current = stack.popLast()
switch current {
case .empty: break
case let .node(left, value, right):
fn(value)
stack.append(left)
stack.append(right)
case .none: // !!!
break
}
}
}
}
Xcode force me to add .none case, but .none is not a constructor of Tree
xxx.swift:9:7: error: switch must be exhaustive
switch current {
^
xxx.swift:9:7: note: add missing case: '.none'
switch current {
Why?
The problem is not that the enumeration is recursive, but that the popLast() method returns an optional (which is nil if the array is empty). Therefore the possible case for current are
case .some(.empty):
case .some(.node(left, value, right)):
case .none: // Or equivalently: case nil:
As of Swift 5.1, enum cases can be matched against non-optional enum patterns (compare SR-7799) so that this simplifies to
case .empty:
case .node(left, value, right):
case .none: // Or equivalently: case nil:
This explains the compiler error and the fix-it.
However, the nil case cannot occur because you check that the array is not empty. Here are three possible solutions:
Since you already checked that the stack is not empty you can force-unwrap safely
while !stack.isEmpty {
let current = stack.popLast()!
switch current {
// ...
Use removeLast() instead. This method expects that the array is not empty, and returns a (non-optional) array element:
while !stack.isEmpty {
let current = stack.removeLast()
switch current {
// ...
Use popLast() with optional binding in the while-condition:
while let current = stack.popLast() {
switch current {
// ...

Extract value from enum using guard or if case in Swift

I have enum with cases:
case one(value: myClassOne)
case two(value: myClassTwo)
I want to check value of that enum. Now i ended with:
switch self.model! {
case .one:
// Great, my case here
default:
break
}
But i rather want to do something like:
if case self.model(let value) is myClassOne { // do smth } // Not compiling
Is there is easy way to simply extract enum value and check it for some condition or class equality?
This is the correct syntax:
if case .one(value: let value) = self.model {
// do something with `value`
}
guard case .one(value: let value) = self.model else {
//handle case where self.model != .one
}
// do something with `value`

How to pattern match enum in a single line in Swift?

In Swift, if I have an enum:
enum MyEnum {
case foo(FooType)
case bar(BarType)
}
I can pattern match with either switch
switch enumValue {
case .foo(let fooValue):
// ... use fooValue
case .bar(let barValue):
// ...
}
...or case let
if case let .foo(fooValue) = enumValue {
// ... use fooValue
}
QUESTION: is it possible to match in one expression to check if it's of type .foo(FooType) to produce an optional FooType??
The equivalent multiline version is:
var x: FooType?
if case let .foo(fooValue) = enumValue {
x = fooValue
}
Something to the effect of
let .foo(x) = enumValue or nil
where fooValue is bound to a FooType? value or nil if it's not .foo.

What are the advantages/use cases of optional patterns introduced in swift 2?

For the simple cases like if let or guard I don't see the advantage,
if case let x? = someOptional where ... {
...
}
//I don't see the advantage over the original if let
if let x = someOptional where ... {
...
}
For the for-case-let case to simplify working with optional collections, I really hope swift can go one step further:
for case let x? in optionalArray {
...
}
//Wouldn't it be better if we could just write
for let x? in optionalArray {
...
}
After google it for a while the only case I find useful is this "Swift 2 Pattern Matching: Unwrapping Multiple Optionals" :
switch (username, password) {
case let (username?, password?):
print("Success!")
case let (username?, nil):
print("Password is missing")
...
So any other advantages of introducing optional patterns?
I believe you're conflating two different concepts. Admittedly, the syntax isn't immediately intuitive, but I hope their uses are clarified below.
(I recommend reading the page about Patterns in The Swift Programming Language.)
case conditions
The "case condition" refers to the ability to write:
if case «pattern» = «expr» { ... }
while case «pattern» = «expr» { ... }
for case «pattern» in «expr» { ... }
These are particularly useful because they let you extract enum values without using switch.
Your example, if case let x? = someOptional ..., is a valid example of this, however I believe it's most useful for enums besides Optional.
enum MyEnum {
case A
case B(Int)
case C(String)
}
func extractStringsFrom(values: [MyEnum]) -> String {
var result = ""
// Without case conditions, we have to use a switch
for value in values {
switch value {
case let .C(str):
result += str
default:
break
}
}
// With a case condition, it's much simpler:
for case let .C(str) in values {
result += str
}
return result
}
You can actually use case conditions with pretty much any pattern that you might normally use in a switch. It can be weird sometimes:
if case let str as String = value { ... } (equivalent to if let str = value as? String)
if case is String = value { ... } (equivalent to if value is String)
if case 1...3 = value { ... } (equivalent to if (1...3).contains(value) or if 1...3 ~= value)
The optional pattern, a.k.a. let x?
The optional pattern, on the other hand, is a pattern that lets you unwrap optionals in contexts besides a simple if let. It's particularly useful when used in a switch (similar to your username/password example):
func doSomething(value: Int?) {
switch value {
//case 2: // Not allowed
case 2?:
print("found two")
case nil:
print("found nil")
case let x:
print("found a different number: \(x)")
}
}