# Enum Case Key Paths: An Update

**URL:** <https://forums.swift.org/t/enum-case-key-paths-an-update/68436>\
**Category:** Discussion\
**Created:** [November 13, 2023, 7:02pm UTC](https://forums.swift.org/t/enum-case-key-paths-an-update/68436 "2023-11-13T19:02:05Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![mbrandonw](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/mbrandonw/32/349_2.png) [@mbrandonw](https://forums.swift.org/u/mbrandonw)\
**Post date:** [November 13, 2023, 7:02pm UTC](https://forums.swift.org/t/enum-case-key-paths-an-update/68436/1 "2023-11-13T19:02:05Z")

</div>

There has been a lot of discussions about better supporting key paths for enums on the forums ([here](https://forums.swift.org/t/enum-case-keypaths/60899), [here](https://forums.swift.org/t/extract-payload-for-enum-cases-having-associated-value/27606), [here](https://forums.swift.org/t/automatically-derive-properties-for-enum-cases/10843), [here](https://forums.swift.org/t/standardizing-case-paths/53335), [here](https://forums.swift.org/t/discussion-inclusion-of-casepaths-into-the-language/53024), [here](https://forums.swift.org/t/pitch-is-case-expressions/64185), [here](https://forums.swift.org/t/pitch-enum-quality-of-life-improvements/52770), and more…), but we wanted to share some work we have done to make things as nice as we can using Swift macros.

We have been able to employ macros to generate actual key paths for enum cases, which we call “case key paths.” While not quite as good as first class language support, we thought it might be interesting to share the full case key path functionality and use cases, including dynamic "case" lookup.

We have defined a [`@CasePathable`](https://swiftpackageindex.com/pointfreeco/swift-case-paths/1.1.0/documentation/casepaths/casepathable()) macro in our [CasePaths](https://github.com/pointfreeco/swift-case-paths) package, which automatically generates actual key paths to an enum's cases:

```swift
@CasePathable
enum Destination {
  case activity(ActivityModel)
  case settings(SettingsModel)
}

\Destination.Cases.activity // CaseKeyPath<Destination, ActivityModel>

```

These key paths can be used with enum values via familiar subscripting:

```swift
let destination = Destination.activity(ActivityModel())
destination[case: \.activity] // Optional(ActivityModel)
destination[case: \.settings] // nil

```

But are also extended with some extra functionality, including `callAsFunction` for embedding new payloads:

```swift
let activityPath = \Destination.Cases.activity

activityPath(ActivityModel()) // Destination.activity(ActivityModel())

```

Otherwise these case key paths can be used in all the same ways as regular key paths, such as writing generic algorithms over the “shape” of enums, and even dynamic member lookup.

## Case study: Easy enum case checking and extraction

By applying the `@CasePathable` macro to an enum it gets access to an `is` method for checking whether or not an enum value matches a particular case:

```swift
let destination = Destination.activity(ActivityModel())

if destination.is(\.activity) // true

```

And further, an enum can opt into making “getter” properties available for each case, which extract the payload from an enum via `@dynamicMemberLookup` and a default dynamic member subscript:

```swift
@CasePathable
@dynamicMemberLookup
enum Destination {
	…
}

let destination = Destination.activity(ActivityModel())
destination.activity // Optional(ActivityModel)
destination.settings // nil

```

This can clean up statement-heavy code with a syntax folks have been asking for for a long time on these forums:

```swift
let destinations: [Destination] = […]
destinations.compactMap(\.activity) // [ActivityModel]

```

## Case study: SwiftUI Bindings

Case key paths make it possible to drive SwiftUI navigation from an enum rather than a bunch of independent optionals. For example, if a view can navigate to two different, mutually exclusive features, it would be best to model this like so:

```swift
struct FeatureView: View {
  @State var destination: Destination?

  enum Destination {
    case activity(ActivityModel)
    case settings(SettingsModel)
  }

  …
}

```

But it can be difficult to construct bindings to each case of the `Destination` enum so that you can use the `sheet(item:)`, `popover(item:)` (and more) view modifiers.

But if you an enum is annotated with `@CasePathable`

```swift
@CasePathable
enum Destination {
	// ...
}

```

…then we can leverage “dynamic case lookup” on bindings to allow them to be transformed into the shape SwiftUI’s existing view modifiers expect via dot-chaining syntax:

```swift
.sheet(item: self.$destination.activity) { model in 
  ActivityView(model: model)
}
.popover(item: self.$destination.settings) { model in 
  SettingsView(model: model)
}

```

It is also possible to drive form controls, like `TextField`s and `Toggle`s, using a `String` or `Bool` that would otherwise be trapped inside an enum case:

```swift
@CasePathable
enum Status {
  case inStock(quantity: Int)
  case outOfStock(isOnBackOrder: Bool)
}

@Binding var status: Status

switch self.item.status {
case .inStock:
  $status.inStock.map { $quantity in
    Section {
      Stepper("Quantity: \(quantity)", value: $quantity)
      Button("Mark as sold out") {
        status = .outOfStock(isOnBackOrder: false)
      }
    } header: { Text("In stock") }
  }
case .outOfStock:
  $status.outOfStock.map { $isOnBackOrder in
    Section {
      Toggle("Is on back order?", isOn: $isOnBackOrder)
      Button("Is back in stock!") {
        status = .inStock(quantity: 1)
      }
    } header: { Text("Out of stock") }
  }
}

```

If you want to try any of this out, our [SwiftUINavigation](https://github.com/pointfreeco/swiftui-navigation/) library has been updated to [define dynamic member lookup](https://github.com/pointfreeco/swiftui-navigation/blob/a3aa5d46e8bf174d773d23b030f1c103999398ac/Sources/SwiftUINavigation/Binding.swift#L13-L15) on `Binding` with a `CaseKeyPath`.

## Case study: Composing App Features

The main impetus for us developing case paths nearly 4 years ago was our [Composable Architecture](https://github.com/pointfreeco/swift-composable-architecture) library, which provides a structured way of defining features and composing them together. Features use enums to enumerate all the possible user actions in an application, and these enums nest through layers of parent/child domains, and case paths were necessary to write code that could abstractly glue these features together.

We have also updated this library to use case key paths, which allows one to compose features together by isolating child state and actions using simple and familiar key path syntax:

```diff
 Reduce { state, action in 
   // ...
 }
-.ifLet(\.child, action: /Action.child) {
+.ifLet(\.child, action: \.child) {
   ChildFeature()
  }

```

This allows us to take advantage of all the nice things native key paths give us, like Xcode autocomplete and type inference.

* * *

We hope achieving this syntax and showing these use cases further motivates the eventual inclusion of case key paths into the language, and we hope folks come up with more interesting use cases to share.

---

<div class="post-metadata">

**Author:** ![davdroman](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/davdroman/32/46501_2.png) [@davdroman](https://forums.swift.org/u/davdroman)\
**Post date:** [November 13, 2023, 9:47pm UTC](https://forums.swift.org/t/enum-case-key-paths-an-update/68436/2 "2023-11-13T21:47:13Z")

</div>

Truly amazing ergonomic improvements all around. Feels the closest it can possibly get to the ideal API without actually being built into Swift itself. I still can't get over how elegantly `@CasePathable` interacts with `@dynamicMemberLookup`; composable Swift at its finest 👌

---

<div class="post-metadata">

**Author:** ![thomasmarsh](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/thomasmarsh/32/37785_2.png) [@thomasmarsh](https://forums.swift.org/u/thomasmarsh)\
**Post date:** [October 16, 2024, 2:28pm UTC](https://forums.swift.org/t/enum-case-key-paths-an-update/68436/3 "2024-10-16T14:28:58Z")

</div>

Amazing work on CasePaths to bring it to this point. I see that the pitch discussion stalled two years ago. Are there any more recent status updates on the pitch?

---

<div class="post-metadata">

**Author:** ![stephencelis](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/stephencelis/32/2675_2.png) [@stephencelis](https://forums.swift.org/u/stephencelis)\
**Post date:** [October 16, 2024, 5:43pm UTC](https://forums.swift.org/t/enum-case-key-paths-an-update/68436/4 "2024-10-16T17:43:12Z")

</div>

I'm not aware of any work being done on this front by the core team, but maybe a member could give us an update. The last I'm aware of was @Alejandro's [open pull request](https://github.com/swiftlang/swift/pull/58940) from around the same time, which introduces the "extract" functionality of case paths. Without "embed" functionality, though, the majority of the tools we've built around case paths are not possible to implement.
