Why switch does not accept case nil in optional? - swift

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.

Related

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 {
// ...

Filter array of items by enum property with associated value

class MyClass: Decodable {
let title: String?
let type: MyClass.MyType?
enum MyType {
case article(data: [Article])
case link(data: [LinkTile])
case none
}
}
I would like to filter an array of MyClass items, so the filtered array won't contain instances with type .none
let filteredArray = array.filter { $0.type != .none } // this doesn't work
Unfortunately, you can't use == with enums with associated values. You need to use pattern matching, but that needs to be done in a switch or if statement.
So, that leads to something ugly like this:
let filteredArray = array.filter { if case .none = $0.type! { return false }; return true }
Notes:
You can't name your enum Type because it conflicts with the built-in Type. Change it to something like MyType.
It is terribly confusing to use none as a case in a custom enum because it gets confused (by the humans) with none in an optional. This is made worse by the fact that your type property is optional. Here I have force unwrapped it, but that is dangerous of course.
You could do:
if case .none? = $0.type
This would match the none case explicitly and treat nil as something you want to keep.
To filter out nil and .none, you could use the nil coalescing operator ??:
if case .none = ($0.type ?? .none)
I would suggest declaring type as MyClass.MyType instead of MyClass.MyType?.
I made you a simple example of how to use enum in your context with a filter function.
enum Foo {
case article(data: [Int])
case link(data: [String])
case `none`
static func myfilter(array: [Foo]) -> [Foo]{
var newArray:[Foo] = []
for element in array {
switch element {
case .article(let article):
newArray.append(.article(data: article))
case .link(let link):
newArray.append(.link(data: link))
case .none:
break
}
}
return newArray
}
}
let foo: [Foo] = [.article(data: [1,2,3]), .link(data: ["hello", "world"]), .none]
print(Foo.myfilter(array: foo))
I made a code which you can compile and test, you have to change the type for Foo, articleand link.
When you want to use an enum, you have to use switch case.
If you absolutely want to use the filter in swift you can, but you need to implement the protocol Sequence which is more complicated in this case.
For each case of your enum, you have to manage a case which use the concept of pattern matching. It is very powerful.

Can I make a class that participates in "if let" constructs the way the Optional does?

I can use if let construct to unwrap optionals, like this:
if let x = someOptional {
// x represents the value of someOptional, and is known to be non-nil
}
In addition to doing the unwrapping, Swift compiler knows to treat the let x = someOptional construct as a logical value.
Is the logic for Optional built into the compiler itself, or is it possible to define my own class with the same behavior?
In other words, can I write something like this:
class MyLogical : SomeProtocolForSwiftToKnowHowToTreatMyClassAsBool {
...
var boolValue : Bool {
return ... // true or false, depending on the state of my object
}
}
and have Swift treat it like a logical value that I can put inside an if condition, with or without let?
if let x = getMyLogical() {
assert(x.boolValue) // boolValue must evaluate to true to get here
}
Note: I remember there being a LogicalValue protocol in an earlier version of Swift, and Optional used to conform to that protocol. LogicalValue is gone now, and there is no replacement (BooleanType has the boolValue property now, but Apple discourages conformance to the BooleanType protocol unless your class represents a simple Boolean value).
Swift's powerful pattern matching system makes something close to this very possible. The case let... pattern does much of what you propose:
if case let x = getMyLogical() where x.boolValue {
assert(x.boolValue) // boolValue must evaluate to true to get here
}
It works with guard statements too:
guard case let x = getMyLogical() where x.boolValue else { fatalError() }
assert(x.boolValue) // boolValue must evaluate to true to get here
In other words, the case let... syntax can be used to conditionally bind any value to any name for use in an if block or after a guard statement.
And I suppose for completeness, it works inside a switch block as well:
switch getMyLogical() {
case let x where x.boolValue:
assert(x.boolValue) // boolValue must evaluate to true to get here
default:
break
}
And as the logical test for a loop:
while case let x = getMyLogical() where x.boolValue {
assert(x.boolValue) // boolValue must evaluate to true to get here
}
For a long time I thought switch statements were the only place you could use case let... But they can be used in other places, too.
I'll add one final note. The case let... pattern can even be used to unwrap optionals:
let num: Int? = 42
if case let x? = num {
print(x)
}
The if let... syntax for unwrapping optionals appears to be a special case of the general case let... pattern matching syntax, an added bit of syntactic sugar to make optionals even easier to work with, but not fundamentally different than the more general case let... functionality.

Switch testing a non optional against optional case values

I know it is possible to test an optional vs optionals as described in this question: Swift: testing against optional value in switch case
But what I want to do is test a non-optional value with optional case values:
switch nonOptionalView{
case optionalView1:
// Do something
case optionalView2:
// Do something else
}
Xcode returns an error "Value of Optional xxx not unwrapped"
and If I add a "?" at the end of the case statement, I get "? pattern cannot match values of type 'UIView'"
Currently pattern matching optional values isn't supported in the Swift standard library. However, you could easily execute your logic using if/else if statements:
if nonOptionalView === optionalView1 {
// Do something
} else if nonOptionalView === optionalView2 {
// Do something else
}
If you'd really like to be able to pattern match optional values, you can use operator overloading. To overload the pattern matching operator ~=, place the following code in your project:
public func ~=<T : Equatable>(a: T?, b: T?) -> Bool {
return a == b
}
Now you'll be able to pattern match optional values just fine:
switch nonOptionalView {
case optionalView1:
// Do something
case optionalView2:
// Do something else
default:
// Handle default case
}
You could take a look at this solution https://stackoverflow.com/a/26941591/2485238
I find the code is more readable than the if/else solution.
In your case it would be
switch nonOptionalView {
case .Some(optionalView1):
// Do something
case .Some(optionalView2):
// Do something else
default:
// Handle default case
}

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