I've written the following view model with a switch statement. I'm getting a compiler warning that doesn't make sense to me.
import Cocoa
class ViewModel {
enum Mode {
case initializing(listenerUUID: UUID)
case loading(listenerUUID: UUID, currentIndex: Int)
case loaded(currentIndex: Int)
case done
case error
}
var mode = Mode.done
private static let firstIndex = 1
private static let stepsize = 25
internal func eventDidPop(_ event: Data, uuid: UUID) {
let newIndex: Int
switch self.mode {
case .initializing(let uuidOrigin), .loading(let uuidOrigin, _):
// See if this event is for us
guard uuidOrigin == uuid else {
return
}
fallthrough
case .initializing(_):
newIndex = Self.firstIndex
case .loading(_, let currentIndex):
newIndex = currentIndex + Self.stepsize
default:
return
}
print("New index = \(newIndex)")
}
}
On line 24 and 26, I get a compiler warning "Case is already handled by previous patterns; consider removing it". But I don't think that compiler warning is appropriate, and I try and indicate that with the fallthrough.
Who is right, the compiler or me? If it's me, I wish to silence the compiler, does anyone know a way?
The fallthrough keyword simply causes code execution to move to the statements inside the next case, without checking case conditions again.
Which means that in your code, if .loading(let uuidOrigin, _) matches in the first case and the uuid is correct then the statements from case .initializing(_) will be executed next. That is probably not what you want.
What you perhaps want is something like this:
switch self.mode {
case .initializing(let uuidOrigin) where uuidOrigin == uuid:
newIndex = Self.firstIndex
case .loading(let uuidOrigin, let currentIndex) where uuidOrigin == uuid:
newIndex = currentIndex + Self.stepsize
default:
return
}
@RonAvitzur I'm definitely not used to this syntax O_O
This must be the most compact code way of handling this. Somehow I know I should immediately understand what was happening here, it's just a switch. But still it took me multiple re-reads to figure out what was happening here.
I have a few more tips that can help with switch clarity.
If you want a case that should be genuinely impossible to match unless something changes in the future, mark it with @unknown. This tells the compiler to warn you if it starts being matchable in the future (like when adding a new enumeration case) without forcing you to actually handle it to compile.
If I have a sensible default, but don’t want to risk forgetting to replace it with something more specific in the future, I often use a pattern like this:
case .one, .two:
// specific action
case .four:
// specific action
case .three, .five:
fallthrough
@unknown default:
// general case
If I add a .six case to the enumeration in the future, I'll get a warning so I know to go back and determine if a specific action is needed. If it isn't, I add it to the fallthrough case.
If you have a case that in theory won’t ever be matched, but technically could be, use assertionFailure(_:file:line:), preconditionFailure(_:file:line:), or fatalError(_:file:line:).