However, something can even be done about that boilerplate. As we have seen, the boilerplate in creating case paths has to do with the extract function, which tries to extract an associated value from an enum. The embed function comes for free in Swift because each case of an enum acts as a function that can embed the associated value into the enum, but the extract takes some work.
One way to try to get rid of this boilerplate is to turn to code generation. In fact, this is what we did in our episode on enum properties, where we showed how we could give enums an API to access the data it held that had the same ergonomics as simple dot syntax that structs can use. The API came with some boilerplate, and so we then a tool over a few episodes (part 1, part 2, part 3) that uses Swift Syntax to analyze our source code and automatically generate the enum properties for all of the enums in our project.
That was really powerful, and we could maybe turn to code generation for case paths, but also code generation is quite heavy. We need to find the best way to run the tool whenever source code changes to make sure it’s up to date, and that can complicate the build process.
It turns out, for case paths in particular we can do something different. We can magically derive the extract
function for a case path from just the embed function that comes from the case of an enum. We say this is “magical” because it uses Swift’s runtime reflection capabilities.
If you are not familiar with the idea of reflection in programming, all you need to know is it allows you to inspect the internal structure of values and objects at runtime. For example, you can use reflection to get a list of all of the string names of stored properties that a struct has.
Any time you use reflection in Swift you are purposely going outside the purview of the Swift compiler. This means you are in dangerous waters since the compiler doesn’t have your back. However, if you tread lightly and write lots of tests, you can come up with something somewhat reasonable that can clear away all of the repetitive boilerplate. Let’s start by exploring the reflection API a bit and see what is available to us.