Last week we explored performance in the Composable Architecture. We looked at the tools it comes with that help you troubleshoot and improve application performance, and we also fixed some longstanding performance problems that existed in the library itself.
But we’re still not quite done making performance improvements, because just after we recorded and edited that episode we found yet another opportunity to eke out more performance. There’s another part of the library that isn’t as efficient as it could be, and that’s in a dependency that the library heavily leans on to earn the “composable” in its name: and that’s Case Paths.
Case paths are a topic we introduced more than a year and a half ago when we theorized what key paths would like look for enums. Swift’s key paths are a wonderful feature that allow you to write algorithms over the shape of a struct by isolating a single field from the rest. Our case paths do the same, except they isolate a single case from the rest of an enum.
We were even able to make case paths as ergonomic as key paths. Just as the compiler generates a key path for each field of a struct automatically, we were able to automatically generate a case path for each case of an enum by making use of Swift’s reflection APIs. We even introduced a prefix operator so that the actual syntax looks similar to key paths.
In those episodes we stressed that reflection can be difficult to get write since you are operating outside the purview of the compiler, but even worse, it can also be quite slow. Using reflection APIs creates a lot of unnecessary objects, and unfortunately this penalty shows up in case paths.