Is it possible to write compound switch case between enum associated value conditional and another enum case?

Viewed 452

For demo purposes I've created next code:

enum WeatherType {
    case cloudy(coverage: Int)
    case sunny
    case rainy
}

let today: WeatherType = .cloudy(coverage: 0)

switch today {
case .cloudy(let coverage) where coverage == 0, .sunny:   // <-- This line doesn't compile
    print("☀️")
case .cloudy(let coverage) where 1...100 ~= coverage:
    print("☁️")
case .rainy:
    print("")
default:
    print("Unknown weather")
}

The compile error message is 'coverage' must be bound in every pattern. As I've already googled that a way to go with associated values is to compare different states of values within the same enum case. But this may lead to code duplication, like in my example, where I need to write two case statements for .sunny and .cloudy(let coverage) where coverage == 0.

Is there any correct, swifty way, to deal with such cases?

3 Answers

You don't need a where-clause to match .cloudy(coverage: 0), just

case .cloudy(coverage: 0), .sunny: 
    print("☀️")

Another option is to use fallthrough, for example

case .cloudy(let coverage) where coverage < 10:
    fallthrough
case .sunny:
    print("☀️")

Martin is right (+1).

But I might suggest a further refinement, moving your switch into an an extension of WeatherType (which I’d probably just call Weather) so that you don’t have to repeat this switch statement every time you’re looking for the symbol:

enum Weather {
    case cloudy(Int)
    case sunny
    case rainy
}

extension Weather {
    var symbol: String {
        switch self {
        case .cloudy(0), .sunny:
            return "☀️"
        case .cloudy:
            return "☁️"
        case .rainy:
            return ""
        }
    }
}

Then you can do:

let today: Weather = .cloudy(0)
print(today.symbol)

And even if you wanted to consider anything less than 10 as sunny, you still don’t need where clause:

extension Weather {
    var symbol: String {
        switch self {
        case .cloudy(..<10), .sunny:
            return "☀️"
        case .cloudy:
            return "☁️"
        case .rainy:
            return ""
        }
    }
}

Pattern matches are composable, so you can do this:

switch today {
case .sunny, .cloudy(0):
    print("☀️")
case .cloudy(1...10):
    print("☁️")
case .rainy:
    print("")
default:
    print("Unknown weather")
}
Related