Extract value in Swift switch - swift

Is it possible in Swift to pattern match and extract the initial value (now cast) at the same time?
For example, if I had these enums:
enum Inner {
case a(Int)
case b(Int)
}
enum Outer {
case one
case two(Inner)
}
and I wanted to match Outer.two(Inner.a(1)) and have have a variable cast to that at the same time
let value: Any // Could be anything :|
switch value {
case let a as Outer.two(Inner.a(1)):
// Do something which needs `a`
default"
// Do some other things
}
Obviously, that doesn't work because as only casts to types.
I've also tried
case Outer.two(let a) where a == Inner.a(1):
case let Outer.two(.a(1)) = a: // i.e. the syntax in "guard case let ..."
which don't work. (NB The first would work, but implementing == isn't an option for me, annoyingly)
FYI: Scala lets you do it with the # operator, something like this:
case a # Outer.two(Inner.a(1)):
Is there a syntax which can do this, or do I need to rethink how I'm solving my problem?

I don't think there is an equivalent pattern to
case a # Outer.two(Inner.a(1)): in Swift. Here is a possible
workaround:
switch value {
case Outer.two(Inner.a(1)):
let a = Inner.a(1)
// Do something which needs `a`
default:
// Do some other things
}

Related

Swift Compiler Error: The enum case has a single tuple as an associated value, but there are several patterns here

Building a project in Xcode 11.4 beta 3, I'm getting this Swift Compiler error on an enum:
The enum case has a single tuple as an associated value, but there are several patterns here, implicitly tupling the patterns and trying to match that instead
Source code:
switch result {
case .error(let err):
//
case .value(let staff, let locations): // <-- error on this line
//
}
Result is an generic enum with associated values for .error and .value. In this case, the associated value is a tupple.
public enum Result<T> {
case value(T)
case error(Error)
}
Don't recall seeing this error before, and searching for it did not yield any results. Any ideas?
I found you can also silence this error by treating the associated value more like a tuple by wrapping it in an extra set of brackets:
switch result {
case .error(let err):
//
case .value((let staff, let locations)):
//
}
Ok, figured it out. Seems like enum with associated values, where the value type is a tuple, can no longer be matched on a switch statement like that:
// Works on Xcode 11.3.1, yields error on 11.4 (Swift 5.2)
switch result {
case .error(let err):
//
case .value(let staff, let locations):
//
}
Solution
Values from tuple have to be manually extracted in Xcode 11.4 (Swift 5.2):
// Works on Xcode 11.4
switch result {
case .error(let err):
//
case .value(let tuple):
let (staff, locations) = tuple
//
}
This is a known issue: https://developer.apple.com/documentation/xcode_release_notes/xcode_11_4_release_notes
Code that relies on the compiler automatically tupling a pattern may lead to a compiler error when upgrading to Xcode 11.4, even though the code compiled before. (58425942)
For example, leaving out parentheses when switching on an Optional of a tuple type causes a compiler error:
switch x { // error: switch must be exhaustive
case .some((let a, let b), let c): // warning: the enum case has a
// single tuple as an associated value, but there are several
// patterns here, implicitly tupling the patterns and trying
// to match that instead
...
}
Workaround: Add extra parentheses to explicitly tuple the pattern:
switch x {
case .some(((let a, let b), let c)): // Notice the extra pair of parentheses.
...
}
In Xcode 12 / Swift 5.3, the warning message is greatly improved:
Enum case 'value' has one associated value that is a tuple of 2 elements. Replace (let staff, let locations) with ((let staff, let locations)).
That's far clearer. It tells you what to do, and offers to do it, as a Fix-It. Of course the alternative is to say (let tuple) and pass the tuple into the case's code and deal with it there, but there seems no need to say that explicitly in the warning.
If I may, I'd like to add an answer for the if case version too.
if case let .value(staff, error) = result {
// Do something
}
and then of course ignoring case:
if case let .value(staff, _) = result {
// Do something
}
Swift 5.3
A clean way using typealias because may you have a lot of value in tuple so typealias make the switch case more cleaner
public typealias StaffLocationsData = (let staff, let locations)
switch result {
case .error(let err):
//
case .value(let data):
let staffLocationsData: StaffLocationsData = data
print(staffLocationsData.staff)
print(staffLocationsData.locations)
}
I think instead check the type and get the value in a tuple, you check the value of the condition. This is my example
switch result {
case let .error(err):
// Do something
case let .value(staff, locations): // <-- error on this line
// Do something
}

Why do I need a default case in this Swift switch? [duplicate]

The Apple documentation says
Every switch statement must be exhaustive. That is, every possible
value of the type being considered must be matched by one of the
switch cases.
So in new Xcode I have placed a code like this
println(UInt16.min); // Output : '0'
println(UInt16.max); // Output : '65535'
var quantity : UInt16 = 10;
switch quantity {
case 0...65535: //OR case UInt16.min...UInt16.max:
println();
default:
println();
}
Now if i remove the default section I get a compiler error:
Switch must be exhaustive
Do you want to add missing cases? Fix
So my question is for a case that I have mentioned as case 0...65535: have I not mentioned all the case values for an UInt16 ?? But still I am getting an error ?? Why am I getting this error, Did i miss something ??
Swift only truly verifies that a switch block is exhaustive when working with enum types. Even a switching on Bool requires a default block in addition to true and false:
var b = true
switch b {
case true: println("true")
case false: println("false")
}
// error: switch must be exhaustive, consider adding a default clause
With an enum, however, the compiler is happy to only look at the two cases:
enum MyBool {
case True
case False
}
var b = MyBool.True
switch b {
case .True: println("true")
case .False: println("false")
}
If you need to include a default block for the compiler's sake but don't have anything for it to do, the break keyword comes in handy:
var b = true
switch b {
case true: println("true")
case false: println("false")
default: break
}
Part of why you see that error because the compiler can't verify that switch is exhaustive without running code. The expression 0...65535 creates a ClosedInterval struct, and when the switch statement executes it has to ask that struct if the value quantity is in the interval. There's room for that to change at run time, so the compiler can't check it at compile time. (See the Halting Problem.)
More generally, the compiler can't detect an exhaustive switch for integer values — even if you add specific cases for every integer value (case 0: ... case 1: ... ... case 65535:), it doesn't know your switch is exhaustive. (Theoretically it could, though: consider filing a feature request about this if it's something you'd like to see.)
As it stands, there are two scenarios where Swift can detect completeness and allow you to omit the default clause: enums and value binding in tuples. #NateCook's answer covers enums — if you switch on an enum value and have a case in your switch for every case in the enum, you don't need a default. You also don't need a default label if you switch on a tuple and bind every possible combination of values, as seen in the Swift book:
switch anotherPoint {
case (let x, 0):
println("on the x-axis with an x value of \(x)")
case (0, let y):
println("on the y-axis with a y value of \(y)")
case let (x, y):
println("somewhere else at (\(x), \(y))")
}
You might generalize this rule as "if the type system knows about the possible values of your type, it can detect switch completeness", but the fact that there's a level on which the type system doesn't know the range of possible (e.g.) UInt32 values is sort of splitting hairs...
Swift 4.1. Either you need to specify all cases or Just include default block inside switch statement.
(As of Swift 4.2, and probably earlier): I have a helper function that converts a Bool? into the selectedSegmentIndex for a UISegmentedControl with 2 segments. If the value is nil then neither segment should be selected. My function uses a switch, which returns the appropriate segment index for true or false values, and uses this to explicitly test for the nil and satisfy the compiler's need for it to be exhaustive:
case nil: // only remaining possible value
fallthrough
default:
return UISegmentedControl.noSegment
Technically, the case nil: fallthrough isn't required because the default: will suffice, but this syntax may be useful if you want to explicitly test a value to make the code more self-documenting, or perhaps in another situation.
Check if your enum was initialised as an optional which could be either case or nil

How to check if enum does not match a pattern?

Note that I have read this post but that post uses a switch statement and it is supposed to do something (return true) when the pattern matches. I, on the other hand, don't want to do anything if the pattern matches and use an if-case statement.
I have this enum:
enum MyEnum {
case a
case b(Int)
case c
case d
}
Here's an instance of it:
let myEnum: MyEnum = .a
Now I want to do something if myEnum is not .b. Since .b has an associated value, I can't simply use an if statement check:
if myEnum != .b { // compiler error
// do my thing here
}
So I must use an if-case statement to pattern match it:
if case .b(_) = myEnum {
} else {
// do my thing here
}
But I really hate the use of the empty if clause. That just looks unswifty to me. I tried to naïvely do this:
if case .b(_) != myEnum { // compiler error!
// do my thing here
}
Is there a better way to do this other than using an empty if clause?
I still have code that should run regardless of whether the pattern matches, so a guard statement won't work.
This is purely minimal semantic change of your own code, but note that you can simply "discard" the empty if clause inline with the case pattern matching:
if case .b(_) = myEnum {} else {
// do your thing here
}
or, leaving out the redundant pattern matching for the associated value of case b:
if case .b = myEnum {} else {
// do your thing here
}
This looks somewhat like a guard clause, but without exiting the scope.
You could use a guard:
guard case .b = myEnum else {
// do your stuff here
return
}
The downside is that you have to exit the scope...
What about:
switch myEnum {
case .b(_):
break
default:
// do your thing here
}
Create a var on the enum which calculates if your value is not .b(_):
enum MyEnum {
case a
case b(Int)
case c
case d
var notB: Bool {
switch self {
case .b(_):
return false
default:
return true
}
}
}
MyEnum.a.notB // true
MyEnum.b(1).notB // false
MyEnum.c // true
MyEnum.d // true
Not the greatest answer since there is still a lot of code to do the check, but at least the check is only one line when you actually use it.
You can write a computed property and have it return a bool value depending on case
enum MyEnum {
case a
case b(Int)
case c
var isCaseB: Bool {
switch self {
case .b(_):
return true
default:
return false
}
}
}
then in your code cleanly check:
if !enumVal.isCaseB {
}
I checked the answer you mentioned in your question but I wasn't sure if you meant you didn't want to use a switch statement at all or just that you didn't want to mix it in with your other code. I think this is a nice and clean way to check prior to writing whatever implementation you need to do depending on the case.

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 switch statements need to be exhaustive? [duplicate]

The Apple documentation says
Every switch statement must be exhaustive. That is, every possible
value of the type being considered must be matched by one of the
switch cases.
So in new Xcode I have placed a code like this
println(UInt16.min); // Output : '0'
println(UInt16.max); // Output : '65535'
var quantity : UInt16 = 10;
switch quantity {
case 0...65535: //OR case UInt16.min...UInt16.max:
println();
default:
println();
}
Now if i remove the default section I get a compiler error:
Switch must be exhaustive
Do you want to add missing cases? Fix
So my question is for a case that I have mentioned as case 0...65535: have I not mentioned all the case values for an UInt16 ?? But still I am getting an error ?? Why am I getting this error, Did i miss something ??
Swift only truly verifies that a switch block is exhaustive when working with enum types. Even a switching on Bool requires a default block in addition to true and false:
var b = true
switch b {
case true: println("true")
case false: println("false")
}
// error: switch must be exhaustive, consider adding a default clause
With an enum, however, the compiler is happy to only look at the two cases:
enum MyBool {
case True
case False
}
var b = MyBool.True
switch b {
case .True: println("true")
case .False: println("false")
}
If you need to include a default block for the compiler's sake but don't have anything for it to do, the break keyword comes in handy:
var b = true
switch b {
case true: println("true")
case false: println("false")
default: break
}
Part of why you see that error because the compiler can't verify that switch is exhaustive without running code. The expression 0...65535 creates a ClosedInterval struct, and when the switch statement executes it has to ask that struct if the value quantity is in the interval. There's room for that to change at run time, so the compiler can't check it at compile time. (See the Halting Problem.)
More generally, the compiler can't detect an exhaustive switch for integer values — even if you add specific cases for every integer value (case 0: ... case 1: ... ... case 65535:), it doesn't know your switch is exhaustive. (Theoretically it could, though: consider filing a feature request about this if it's something you'd like to see.)
As it stands, there are two scenarios where Swift can detect completeness and allow you to omit the default clause: enums and value binding in tuples. #NateCook's answer covers enums — if you switch on an enum value and have a case in your switch for every case in the enum, you don't need a default. You also don't need a default label if you switch on a tuple and bind every possible combination of values, as seen in the Swift book:
switch anotherPoint {
case (let x, 0):
println("on the x-axis with an x value of \(x)")
case (0, let y):
println("on the y-axis with a y value of \(y)")
case let (x, y):
println("somewhere else at (\(x), \(y))")
}
You might generalize this rule as "if the type system knows about the possible values of your type, it can detect switch completeness", but the fact that there's a level on which the type system doesn't know the range of possible (e.g.) UInt32 values is sort of splitting hairs...
Swift 4.1. Either you need to specify all cases or Just include default block inside switch statement.
(As of Swift 4.2, and probably earlier): I have a helper function that converts a Bool? into the selectedSegmentIndex for a UISegmentedControl with 2 segments. If the value is nil then neither segment should be selected. My function uses a switch, which returns the appropriate segment index for true or false values, and uses this to explicitly test for the nil and satisfy the compiler's need for it to be exhaustive:
case nil: // only remaining possible value
fallthrough
default:
return UISegmentedControl.noSegment
Technically, the case nil: fallthrough isn't required because the default: will suffice, but this syntax may be useful if you want to explicitly test a value to make the code more self-documenting, or perhaps in another situation.
Check if your enum was initialised as an optional which could be either case or nil