Swift’s switch statement handling is “unsafe” (in a introduces-product-bugs way, not in a unsafe program execution way) due to allowing default
cases in enum switches. This suppresses any diagnostics when a new case is introduced and goes unhandled in the code, which could lead to bugs in the business logic:
enum ScreenType {
case phone
case tablet
case watch // newly added
}
func takes(screen: ScreenType) {
switch color {
case .phone:
// handle phone logic
case tablet:
// handle tablet logic
default:
// failed to update switch to handle watch logic :(
}
}
Clang/Objc has compiler flags to prevent this scenario, but Swift does not, except by using @unknown default
which does require new cases to be handled:
enum ScreenType {
case phone
case tablet
case watch // newly added
}
func takes(screen: ScreenType) {
// WARNING: Switch must be exhaustive
switch color {
case .phone:
// handle phone logic
case tablet:
// handle tablet logic
@unknown default:
}
}
This is a warning, not an error. But many/most codebases in Swift promote warnings to errors.
Aside from the warning, the issue is that there’s no real way to enforce @uknown default
across a codebase instead of default
, because you can’t currently apply it to non-enum switches like:
// Error: Switch must be exhaustive:
// Remove '@unknown' to handle remaining values
switch str {
case "hello":
print("world")
case "foo":
print("bar")
@unknown default:
print("default")
}
Current swift-syntax linters also do not offer a solution, as the type of the switch isn't necessarily specified in explicit annotations. This should also ideally be fixed at the compiler level.
Perhaps there is something I'm overlooking, and I'm not sold on @unknown default being the solution, but it seems like the simplest way to be able to enforce truly exhaustive switches.