Automatic CaseIterable conformance for nested cases that also conform to CaseIterable

I was using CaseIterable to iterate through the enum options and came up with a case that would be nice to solve.

I was attempting to use a nested enum that also conformed to CaseIterable - I don't see any reason why the outer Example enum cannot create a union of it's nested children

enum Example: CaseIterable {
    case a(A), b, c
}

enum A: CaseIterable {
    case d, e, f
}

To work around it you can provide manual conformance:

extension Example {
    static var allCases: [Example] {
        [b, c] + A.allCases.map(Example.a)
    }
}

What are your thoughts?

7 Likes

Hi. I agree that it is strange when two CaseIterable enums loose the ability to be CaseIterable when compounded. But can you please provide some real world examples?
Because "I don't see any reason why the outer Example enum cannot create a union of it's nested children" is not enough motivation.

Some real-world use-cases can include navigation routing and theming, though, I haven't explored more scenarios then that - I am sure there will be lots more

If you want some specific code examples I can share some from my recent work looking into Composable Architecture and defining composable routes using a NavigationLink

enum HomeRoute: CaseIterable, NavigationRoute {
    case music(MusicRoute)
    case settings(SettingsRoute)

    func destination(in: Store<_, _>) -> some View
}

enum MusicRoute: CaseIterable, NavigationRoute {
    case song
    case album

    func destination(in: Store<_, _>) -> some View
}

enum SettingsRoute: CaseIterable, NavigationRoute {
    case user
    case app

    func destination(in: Store<_, _>) -> some View
}

In the example above, I would require access to the cases to configure the navigation links ahead of time so when you come to perform the action of navigating they are already available in the SwiftUI view hierarchy

Thanks for sharing. In my practice I can only find one case. To point out, CaseIterable is needed only as a help feature for writing exhaustive list of test cases. Here is simplified example:

enum DeliveryKind {
  case addressDelivery
  case pickupPoint(kind: PickupPointDeliveryKind)
  case express(kind : ExpressDeliveryKind)
  
  enum PickupPointDeliveryKind {
    case shop
    case post
  }

  enum ExpressDeliveryKind {
    case courier
    case taxi
  }
}

DeliveryTests: XCTest {
  for deliveryKind in DeliveryKind.allCases {
    // test some feature 
  }
}

It will be good to hear from the community other cases, examples and arguments. Seems it is rather simple to implement it in the compiler.

SE-0194 coauthor here.

It would be totally reasonable to support this; we didn't include it in the proposal because (a) we wanted to keep the design and implementation small and simple, and (b) various other reasons that don't really apply anymore.

Specifically... (a) it wasn't clear how much we wanted to focus on enum cases vs. other small iterable types like `Bool` or `Int8`; (b) we were thinking at the time that the default implementation of `allCases` might not return an `Array`, but instead a custom collection that generated the cases lazily, which would be hard to do with associated values; (c) we were trying to strip out unnecessary "bells and whistles" to get to a core proposal that everyone agreed with and thought was justified.

So this would definitely be a natural extension of CaseIterable; the question is mainly if it's worth the trouble. You might want to dig through SE-0194's review and discussion threads to see if there are any points for or against that I've forgotten in the last three-ish years.

7 Likes