<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Daniel Bauke</title><description>Projects, experiences, findings and ideas, in writing and on video.</description><link>https://bauke.dev/</link><item><title>What&apos;s wrong with Swift&apos;s enums?</title><link>https://bauke.dev/posts/whats-wrong-with-enums/</link><guid isPermaLink="true">https://bauke.dev/posts/whats-wrong-with-enums/</guid><description>Enums are data. Protocols are behaviors.</description><pubDate>Sun, 20 Apr 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;tldr&quot;&gt;TL;DR&lt;/h2&gt;
&lt;p&gt;It’s all about the choice between imperative conditions (enums just enforce them) and object-oriented design (here: protocols).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Enums are data&lt;/strong&gt;. By introducing them, you say: &lt;em&gt;I emit the data, and I need to process it in every place where it is used. I will handle endlessly expanding conditional checks every time a new data definition comes.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Protocols are behaviors&lt;/strong&gt;. By introducing them, you say: &lt;em&gt;I want it to do something, but I don’t care how. Once I define inputs and outputs, it’s up to the implementation to fulfill the contract.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;So, think of it like choosing a parenting style: strict control versus granting independence. Supervising every single action will eventually exhaust you and rather not help your kid, while setting clear boundaries and trusting things to work themselves out lets your system grow naturally. And much like raising a family, with each new project you will probably tend to become a calmer, more relaxed parent who learns when to let go.&lt;/p&gt;
&lt;h2 id=&quot;personal-note&quot;&gt;Personal note&lt;/h2&gt;
&lt;p&gt;I suffer from self-discovered and self-diagnosed &lt;em&gt;Conditional Statement Aversion&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;It’s a serious condition, since it is triggered by any use of &lt;code&gt;if-else&lt;/code&gt; or &lt;code&gt;switch&lt;/code&gt; statements in the code. So, basically, &lt;strong&gt;if you want my attention in any code review&lt;/strong&gt;, just include it, and I won’t be able to pass it without trying to remove or simplify it.&lt;/p&gt;
&lt;p&gt;Hence this post: I’m trying to ease my pain.&lt;/p&gt;
&lt;p&gt;I know, it sounds silly, but it has deeper roots. It’s not just about conditionals, but about how to build object-oriented software correctly, which can also be easily transposed to the &lt;a href=&quot;https://martinfowler.com/bliki/TellDontAsk.html&quot;&gt;“Tell, Not Ask”&lt;/a&gt; rule.&lt;/p&gt;
&lt;p&gt;So, let’s dive in! Let me tell you what &lt;strong&gt;is&lt;/strong&gt; wrong with enums.&lt;/p&gt;
&lt;h2 id=&quot;enums&quot;&gt;Enums&lt;/h2&gt;
&lt;p&gt;Enums are a valuable tool in Swift. They provide a means of defining a type with a finite set of values, offering a level of safety and clarity in our code. Swift also makes them very powerful; they can represent errors, handle associated data, and can even be riddled with their own functions.&lt;/p&gt;
&lt;p&gt;And for this reason, the saying “just because you can, doesn’t mean you should,” holds significant weight when considering the use of enums. They are powerful, but there are times when enums might not be the best choice.&lt;/p&gt;
&lt;p&gt;Especially in the dynamic and ever-changing landscape of software development, where the only constant is change, enums can inadvertently couple unrelated concepts, creating dependencies that may be difficult to decouple as requirements evolve.&lt;/p&gt;
&lt;p&gt;While enums might be useful in the initial phase of code design as they help us understand the problem and make it work, they can become a bottleneck as the codebase grows and evolves. They’re great for creating something quickly, but as the software scales and becomes more complex, the limitations of enums can become apparent and even create unexpected breaking changes.&lt;/p&gt;
&lt;h2 id=&quot;protocols&quot;&gt;Protocols&lt;/h2&gt;
&lt;p&gt;In Swift, protocols act as blueprints that define what a conforming type can do. Rather than holding concrete data, a protocol outlines requirements that any adopting type must fulfill: methods, properties, initializers, including inheritance from other protocols. Unlike enums, they don’t contain closed set of values , but provide a contract for certain behaviors where it doesn’t matter how it is implemented.&lt;/p&gt;
&lt;p&gt;Let’s illustrate it with an example. Suppose we’re building an app where we have different routes to navigate through screens, and some of them can also be shared as a link. We may be tempted to use an Enum to represent these routes:&lt;/p&gt;
&lt;figure&gt;&lt;pre class=&quot;astro-code post-css-variables&quot; style=&quot;background-color:var(--sunken);color:var(--ink); overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;swift&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--c-key)&quot;&gt;enum&lt;/span&gt;&lt;span style=&quot;color:var(--c-type)&quot;&gt; AppRoute&lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--c-key)&quot;&gt;    case&lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt; home&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--c-key)&quot;&gt;    case&lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt; profile(userId: &lt;/span&gt;&lt;span style=&quot;color:var(--c-type)&quot;&gt;Int&lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--c-key)&quot;&gt;    case&lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt; settings(section: &lt;/span&gt;&lt;span style=&quot;color:var(--c-type)&quot;&gt;String&lt;/span&gt;&lt;span style=&quot;color:var(--c-key)&quot;&gt;?&lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--c-key)&quot;&gt;    var&lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt; shareURL: URL&lt;/span&gt;&lt;span style=&quot;color:var(--c-key)&quot;&gt;?&lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--c-key)&quot;&gt;        switch&lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt; self {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--c-key)&quot;&gt;        case&lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt; .profile(&lt;/span&gt;&lt;span style=&quot;color:var(--c-key)&quot;&gt;let&lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt; userId)&lt;/span&gt;&lt;span style=&quot;color:var(--c-key)&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt;            URL(string: &lt;/span&gt;&lt;span style=&quot;color:var(--c-str)&quot;&gt;&quot;https://example.com/users/&lt;/span&gt;&lt;span style=&quot;color:var(--c-str)&quot;&gt;\(userId)&lt;/span&gt;&lt;span style=&quot;color:var(--c-str)&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--c-key)&quot;&gt;        case&lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt; .home, .settings&lt;/span&gt;&lt;span style=&quot;color:var(--c-key)&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--c-key)&quot;&gt;            nil&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--c-key)&quot;&gt;func&lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt; navigateTo(route: AppRoute) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--c-key)&quot;&gt;    switch&lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt; route {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--c-key)&quot;&gt;    case&lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt; .home&lt;/span&gt;&lt;span style=&quot;color:var(--c-key)&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--c-com)&quot;&gt;        // Pop to the root screen&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--c-key)&quot;&gt;    case&lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt; .profile(&lt;/span&gt;&lt;span style=&quot;color:var(--c-key)&quot;&gt;let&lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt; userId)&lt;/span&gt;&lt;span style=&quot;color:var(--c-key)&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--c-com)&quot;&gt;        // Push the profile screen for userId&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--c-key)&quot;&gt;    case&lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt; .settings(&lt;/span&gt;&lt;span style=&quot;color:var(--c-key)&quot;&gt;let&lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt; section)&lt;/span&gt;&lt;span style=&quot;color:var(--c-key)&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--c-com)&quot;&gt;        // Present settings modally, scrolled to section if given&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--c-key)&quot;&gt;func&lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt; share(route: AppRoute) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--c-key)&quot;&gt;    guard&lt;/span&gt;&lt;span style=&quot;color:var(--c-key)&quot;&gt; let&lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt; url &lt;/span&gt;&lt;span style=&quot;color:var(--c-key)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt; route.shareURL &lt;/span&gt;&lt;span style=&quot;color:var(--c-key)&quot;&gt;else&lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt; { &lt;/span&gt;&lt;span style=&quot;color:var(--c-key)&quot;&gt;return&lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt; }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--c-com)&quot;&gt;    // Present the share sheet with url&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;This approach works fine until we have a new screen that takes additional parameters. Every time a new route is introduced, or parameters change, we’d need to revisit both &lt;code&gt;navigateTo(route:)&lt;/code&gt; and &lt;code&gt;shareURL&lt;/code&gt;. Plus, it couples unrelated routes together, creating dependencies that can make our code harder to manage. Every case also has to answer every question: &lt;code&gt;home&lt;/code&gt; and &lt;code&gt;settings&lt;/code&gt; can’t be shared, so &lt;code&gt;shareURL&lt;/code&gt; becomes optional and &lt;code&gt;share(route:)&lt;/code&gt; has to check it at runtime.&lt;/p&gt;
&lt;p&gt;Using protocols we can make our routes more flexible and decoupled:&lt;/p&gt;
&lt;figure&gt;&lt;pre class=&quot;astro-code post-css-variables&quot; style=&quot;background-color:var(--sunken);color:var(--ink); overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;swift&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--c-key)&quot;&gt;protocol&lt;/span&gt;&lt;span style=&quot;color:var(--c-type)&quot;&gt; AppRoutable&lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--c-key)&quot;&gt;    func&lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt; navigate()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--c-key)&quot;&gt;protocol&lt;/span&gt;&lt;span style=&quot;color:var(--c-type)&quot;&gt; Shareable&lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--c-key)&quot;&gt;    var&lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt; shareURL: URL { &lt;/span&gt;&lt;span style=&quot;color:var(--c-key)&quot;&gt;get&lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt; }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--c-key)&quot;&gt;struct&lt;/span&gt;&lt;span style=&quot;color:var(--c-type)&quot;&gt; HomeRoute&lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:var(--c-type)&quot;&gt;AppRoutable &lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--c-key)&quot;&gt;    func&lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt; navigate() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--c-com)&quot;&gt;        // Pop to the root screen&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--c-key)&quot;&gt;struct&lt;/span&gt;&lt;span style=&quot;color:var(--c-type)&quot;&gt; ProfileRoute&lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:var(--c-type)&quot;&gt;AppRoutable&lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:var(--c-type)&quot;&gt;Shareable &lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--c-key)&quot;&gt;    let&lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt; userId: &lt;/span&gt;&lt;span style=&quot;color:var(--c-type)&quot;&gt;Int&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--c-key)&quot;&gt;    var&lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt; shareURL: URL {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt;        URL(string: &lt;/span&gt;&lt;span style=&quot;color:var(--c-str)&quot;&gt;&quot;https://example.com/users/&lt;/span&gt;&lt;span style=&quot;color:var(--c-str)&quot;&gt;\(userId)&lt;/span&gt;&lt;span style=&quot;color:var(--c-str)&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:var(--c-key)&quot;&gt;!&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--c-key)&quot;&gt;    func&lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt; navigate() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--c-com)&quot;&gt;        // Push the profile screen for userId&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--c-key)&quot;&gt;struct&lt;/span&gt;&lt;span style=&quot;color:var(--c-type)&quot;&gt; SettingsRoute&lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:var(--c-type)&quot;&gt;AppRoutable &lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--c-key)&quot;&gt;    let&lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt; section: &lt;/span&gt;&lt;span style=&quot;color:var(--c-type)&quot;&gt;String&lt;/span&gt;&lt;span style=&quot;color:var(--c-key)&quot;&gt;?&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--c-key)&quot;&gt;    func&lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt; navigate() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--c-com)&quot;&gt;        // Present settings modally, scrolled to section if given&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--c-key)&quot;&gt;func&lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt; navigateTo(route: &lt;/span&gt;&lt;span style=&quot;color:var(--c-key)&quot;&gt;any&lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt; AppRoutable) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt;    route.navigate()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--c-key)&quot;&gt;func&lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt; share(item: &lt;/span&gt;&lt;span style=&quot;color:var(--c-key)&quot;&gt;any&lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt; Shareable) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--c-com)&quot;&gt;    // Present the share sheet with item.shareURL&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;Now, each route is a struct that conforms to the &lt;code&gt;AppRoutable&lt;/code&gt; protocol. &lt;code&gt;AppRoutable&lt;/code&gt; does one thing: it navigates. It knows nothing about sharing. Sharing lives in a separate, small &lt;code&gt;Shareable&lt;/code&gt; protocol, adopted only by routes that can actually be shared. That’s the Interface Segregation Principle: &lt;code&gt;HomeRoute&lt;/code&gt; and &lt;code&gt;SettingsRoute&lt;/code&gt; don’t have to pretend they have a URL, and &lt;code&gt;share(item:)&lt;/code&gt; accepts only things that can be shared. The compiler now checks what the enum version had to check at runtime.&lt;/p&gt;
&lt;p&gt;Each route also carries only what it needs. &lt;code&gt;HomeRoute&lt;/code&gt; has no properties at all, &lt;code&gt;ProfileRoute&lt;/code&gt; has a &lt;code&gt;userId&lt;/code&gt; and a share link, and &lt;code&gt;SettingsRoute&lt;/code&gt; has an optional section. Each route is responsible only for its own screen, so changing how settings open touches &lt;code&gt;SettingsRoute&lt;/code&gt; and nothing else.&lt;/p&gt;
&lt;p&gt;If a new route is introduced, we just need to create a new struct that conforms to &lt;code&gt;AppRoutable&lt;/code&gt;. The &lt;code&gt;navigateTo(route:)&lt;/code&gt; function doesn’t need to change because it operates on the &lt;code&gt;AppRoutable&lt;/code&gt; protocol, not a specific enum. This approach decouples the routes and provides flexibility.&lt;/p&gt;
&lt;p&gt;Imagine adding a new route - &lt;code&gt;SearchRoute&lt;/code&gt;, that requires a query parameter and can be shared as well:&lt;/p&gt;
&lt;figure&gt;&lt;pre class=&quot;astro-code post-css-variables&quot; style=&quot;background-color:var(--sunken);color:var(--ink); overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;swift&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--c-key)&quot;&gt;struct&lt;/span&gt;&lt;span style=&quot;color:var(--c-type)&quot;&gt; SearchRoute&lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:var(--c-type)&quot;&gt;AppRoutable&lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:var(--c-type)&quot;&gt;Shareable &lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--c-key)&quot;&gt;    let&lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt; query: &lt;/span&gt;&lt;span style=&quot;color:var(--c-type)&quot;&gt;String&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--c-key)&quot;&gt;    var&lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt; shareURL: URL {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt;        URL(string: &lt;/span&gt;&lt;span style=&quot;color:var(--c-str)&quot;&gt;&quot;https://example.com/search&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:var(--c-key)&quot;&gt;!&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt;            .appending(queryItems: [URLQueryItem(name: &lt;/span&gt;&lt;span style=&quot;color:var(--c-str)&quot;&gt;&quot;q&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt;, value: trimmedQuery)])&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--c-key)&quot;&gt;    func&lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt; navigate() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--c-com)&quot;&gt;        // Push the search results for trimmedQuery&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--c-key)&quot;&gt;    private&lt;/span&gt;&lt;span style=&quot;color:var(--c-key)&quot;&gt; var&lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt; trimmedQuery: &lt;/span&gt;&lt;span style=&quot;color:var(--c-type)&quot;&gt;String&lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt;        query.trimmingCharacters(in: .whitespacesAndNewlines)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--c-key)&quot;&gt;let&lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt; searchRoute &lt;/span&gt;&lt;span style=&quot;color:var(--c-key)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt; SearchRoute(query: &lt;/span&gt;&lt;span style=&quot;color:var(--c-str)&quot;&gt;&quot;Swift Protocols&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt;navigateTo(route: searchRoute)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt;share(item: searchRoute)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;In this case, we’ve simply created a new struct, &lt;code&gt;SearchRoute&lt;/code&gt;, that conforms to &lt;code&gt;AppRoutable&lt;/code&gt; and &lt;code&gt;Shareable&lt;/code&gt;. It even has its own private helper, &lt;code&gt;trimmedQuery&lt;/code&gt;, that no other route or caller needs to know about. Neither &lt;code&gt;navigateTo(route:)&lt;/code&gt; nor &lt;code&gt;share(item:)&lt;/code&gt; needs to change. With the enum, the same addition means a new case and an update to every switch: &lt;code&gt;navigateTo(route:)&lt;/code&gt;, &lt;code&gt;shareURL&lt;/code&gt;, and any other one added since.&lt;/p&gt;
&lt;h2 id=&quot;errors-as-enums&quot;&gt;Errors as enums&lt;/h2&gt;
&lt;p&gt;Errors as enums with internal conditionals are strongly baked into daily Swift practices. So, challenging that paradigm is like battling windmills, but I will do it anyway.&lt;/p&gt;
&lt;p&gt;Let’s use this snippet for the further discussions:&lt;/p&gt;
&lt;figure&gt;&lt;pre class=&quot;astro-code post-css-variables&quot; style=&quot;background-color:var(--sunken);color:var(--ink); overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;swift&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--c-key)&quot;&gt;enum&lt;/span&gt;&lt;span style=&quot;color:var(--c-type)&quot;&gt; AppError&lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:var(--c-type)&quot;&gt;Error&lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:var(--c-type)&quot;&gt;CustomStringConvertible &lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--c-key)&quot;&gt;    case&lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt; fileNotFound(name: &lt;/span&gt;&lt;span style=&quot;color:var(--c-type)&quot;&gt;String&lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt;, path: &lt;/span&gt;&lt;span style=&quot;color:var(--c-type)&quot;&gt;String&lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--c-key)&quot;&gt;    case&lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt; invalidData(url: URL)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--c-key)&quot;&gt;    case&lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt; decodingFailed(error: &lt;/span&gt;&lt;span style=&quot;color:var(--c-type)&quot;&gt;Error&lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--c-key)&quot;&gt;    var&lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt; description: &lt;/span&gt;&lt;span style=&quot;color:var(--c-type)&quot;&gt;String&lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--c-key)&quot;&gt;        switch&lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt; self {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--c-key)&quot;&gt;        case&lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt; .fileNotFound(&lt;/span&gt;&lt;span style=&quot;color:var(--c-key)&quot;&gt;let&lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt; name, &lt;/span&gt;&lt;span style=&quot;color:var(--c-key)&quot;&gt;let&lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt; path)&lt;/span&gt;&lt;span style=&quot;color:var(--c-key)&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--c-str)&quot;&gt;            &quot;File `&lt;/span&gt;&lt;span style=&quot;color:var(--c-str)&quot;&gt;\(name)&lt;/span&gt;&lt;span style=&quot;color:var(--c-str)&quot;&gt;.json` could not be found at path: &lt;/span&gt;&lt;span style=&quot;color:var(--c-str)&quot;&gt;\(path)&lt;/span&gt;&lt;span style=&quot;color:var(--c-str)&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--c-key)&quot;&gt;        case&lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt; .invalidData(&lt;/span&gt;&lt;span style=&quot;color:var(--c-key)&quot;&gt;let&lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt; url)&lt;/span&gt;&lt;span style=&quot;color:var(--c-key)&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--c-str)&quot;&gt;            &quot;Failed to load data from URL: &lt;/span&gt;&lt;span style=&quot;color:var(--c-str)&quot;&gt;\(url)&lt;/span&gt;&lt;span style=&quot;color:var(--c-str)&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--c-key)&quot;&gt;        case&lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt; .decodingFailed(&lt;/span&gt;&lt;span style=&quot;color:var(--c-key)&quot;&gt;let&lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt; error)&lt;/span&gt;&lt;span style=&quot;color:var(--c-key)&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--c-str)&quot;&gt;            &quot;Failed to decode JSON: &lt;/span&gt;&lt;span style=&quot;color:var(--c-str)&quot;&gt;\(error.localizedDescription)&lt;/span&gt;&lt;span style=&quot;color:var(--c-str)&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;That small piece of code with conditions to unpack some hidden rules from a type with a closed set of variants breaks several principles of SOLID and OOP.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Single Responsibility Principle&lt;/strong&gt;. Because you define one type with a closed set of variants that is expected to be used in different, potentially unrelated places, it unnecessarily couples them all together with a single type.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Open-Closed Principle&lt;/strong&gt;. There are actually three independent issues here, but they are all related to OCP:
&lt;ul&gt;
&lt;li&gt;Once you need to add another error (variant/case), you can’t add it freely as an independent one somewhere in the code—you have to change the existing enum. And what if someone from outside of the team or even the organization wants to do it? Not a chance! Enums are not extensible by design.&lt;/li&gt;
&lt;li&gt;When you need to handle a property other than &lt;code&gt;description&lt;/code&gt;, you need to add another switch statement and implement it for all cases (or cheat and create a &lt;code&gt;default:&lt;/code&gt; case). This code duplication can quickly accumulate.&lt;/li&gt;
&lt;li&gt;Dedicated types (structs) are much simpler to extend with specific logic if there’s more than one attribute to add or some specific logic (Example: &lt;code&gt;FileOperation&lt;/code&gt; in &lt;code&gt;FileNotFoundError&lt;/code&gt; below).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Tell, Don’t Ask&lt;/strong&gt;. OOP follows this principle a lot. You should instruct the object to perform an action rather than inquire about its state and then decide what to do (in this case, inquiring the data to get the &lt;code&gt;description&lt;/code&gt;).&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;There’s also good &lt;a href=&quot;https://forums.swift.org/t/struct-vs-enum-for-error-types/78303&quot;&gt;discussion on Swift Forum&lt;/a&gt; about it, which can be summarized in the following points:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Adding a new &lt;code&gt;case&lt;/code&gt; is a source-breaking change for clients using exhaustive switching&lt;/li&gt;
&lt;li&gt;Adding associated values to an existing &lt;code&gt;case&lt;/code&gt; is an API-breaking change&lt;/li&gt;
&lt;li&gt;Adding extra fields is cumbersome compared to structs&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The usual answer to the first point is &lt;code&gt;@unknown default&lt;/code&gt;. It softens the break: a missing case becomes a warning instead of an error. But the code still compiles, and at runtime the new case falls into a catch-all branch that knows nothing about it. You trade a compile error for a silent fallback, and every &lt;code&gt;switch&lt;/code&gt; still needs a fix once someone reads the warnings.&lt;/p&gt;
&lt;h3 id=&quot;errors-as-structs&quot;&gt;Errors as structs&lt;/h3&gt;
&lt;p&gt;Here’s the same set of errors, split into one struct per error behind a shared protocol:&lt;/p&gt;
&lt;figure&gt;&lt;pre class=&quot;astro-code post-css-variables&quot; style=&quot;background-color:var(--sunken);color:var(--ink); overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;swift&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--c-key)&quot;&gt;protocol&lt;/span&gt;&lt;span style=&quot;color:var(--c-type)&quot;&gt; AppError&lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:var(--c-type)&quot;&gt;Error&lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:var(--c-type)&quot;&gt;CustomStringConvertible &lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt;{}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--c-key)&quot;&gt;struct&lt;/span&gt;&lt;span style=&quot;color:var(--c-type)&quot;&gt; InvalidDataError&lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:var(--c-type)&quot;&gt;AppError &lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--c-key)&quot;&gt;    let&lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt; url: URL&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--c-key)&quot;&gt;    var&lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt; description: &lt;/span&gt;&lt;span style=&quot;color:var(--c-type)&quot;&gt;String&lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--c-str)&quot;&gt;        &quot;Failed to load data from URL: &lt;/span&gt;&lt;span style=&quot;color:var(--c-str)&quot;&gt;\(url)&lt;/span&gt;&lt;span style=&quot;color:var(--c-str)&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--c-key)&quot;&gt;struct&lt;/span&gt;&lt;span style=&quot;color:var(--c-type)&quot;&gt; DecodingFailedError&lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:var(--c-type)&quot;&gt;AppError &lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--c-key)&quot;&gt;    let&lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt; error: &lt;/span&gt;&lt;span style=&quot;color:var(--c-type)&quot;&gt;Error&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--c-key)&quot;&gt;    var&lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt; description: &lt;/span&gt;&lt;span style=&quot;color:var(--c-type)&quot;&gt;String&lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--c-str)&quot;&gt;        &quot;Failed to decode JSON: &lt;/span&gt;&lt;span style=&quot;color:var(--c-str)&quot;&gt;\(error.localizedDescription)&lt;/span&gt;&lt;span style=&quot;color:var(--c-str)&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--c-key)&quot;&gt;struct&lt;/span&gt;&lt;span style=&quot;color:var(--c-type)&quot;&gt; FileNotFoundError&lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:var(--c-type)&quot;&gt;AppError &lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--c-key)&quot;&gt;    struct&lt;/span&gt;&lt;span style=&quot;color:var(--c-type)&quot;&gt; FileOperation&lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:var(--c-type)&quot;&gt;OptionSet &lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--c-key)&quot;&gt;        let&lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt; rawValue: &lt;/span&gt;&lt;span style=&quot;color:var(--c-type)&quot;&gt;Int&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--c-key)&quot;&gt;        static&lt;/span&gt;&lt;span style=&quot;color:var(--c-key)&quot;&gt; let&lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt; read &lt;/span&gt;&lt;span style=&quot;color:var(--c-key)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt; FileOperation(rawValue: 1 &lt;/span&gt;&lt;span style=&quot;color:var(--c-key)&quot;&gt;&amp;#x3C;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt; 0)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--c-key)&quot;&gt;        static&lt;/span&gt;&lt;span style=&quot;color:var(--c-key)&quot;&gt; let&lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt; write &lt;/span&gt;&lt;span style=&quot;color:var(--c-key)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt; FileOperation(rawValue: 1 &lt;/span&gt;&lt;span style=&quot;color:var(--c-key)&quot;&gt;&amp;#x3C;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt; 1)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--c-key)&quot;&gt;        static&lt;/span&gt;&lt;span style=&quot;color:var(--c-key)&quot;&gt; let&lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt; delete &lt;/span&gt;&lt;span style=&quot;color:var(--c-key)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt; FileOperation(rawValue: 1 &lt;/span&gt;&lt;span style=&quot;color:var(--c-key)&quot;&gt;&amp;#x3C;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt; 2)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--c-key)&quot;&gt;    let&lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt; name: &lt;/span&gt;&lt;span style=&quot;color:var(--c-type)&quot;&gt;String&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--c-key)&quot;&gt;    let&lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt; path: &lt;/span&gt;&lt;span style=&quot;color:var(--c-type)&quot;&gt;String&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--c-key)&quot;&gt;    var&lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt; operations: FileOperation &lt;/span&gt;&lt;span style=&quot;color:var(--c-key)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt; .read&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--c-key)&quot;&gt;    var&lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt; description: &lt;/span&gt;&lt;span style=&quot;color:var(--c-type)&quot;&gt;String&lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--c-str)&quot;&gt;        &quot;File `&lt;/span&gt;&lt;span style=&quot;color:var(--c-str)&quot;&gt;\(name)&lt;/span&gt;&lt;span style=&quot;color:var(--c-str)&quot;&gt;.json` could not be found at path: &lt;/span&gt;&lt;span style=&quot;color:var(--c-str)&quot;&gt;\(path)&lt;/span&gt;&lt;span style=&quot;color:var(--c-str)&quot;&gt; during &lt;/span&gt;&lt;span style=&quot;color:var(--c-str)&quot;&gt;\(operations)&lt;/span&gt;&lt;span style=&quot;color:var(--c-str)&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;h3 id=&quot;limitations-in-errors-as-structs&quot;&gt;Limitations in errors as structs&lt;/h3&gt;
&lt;p&gt;One limitation of typed throws in Swift 6: a function can name only one error type. &lt;code&gt;throws(FileNotFoundError)&lt;/code&gt; works fine, but you can’t write &lt;code&gt;throws(any AppError)&lt;/code&gt;. A function that can fail in several ways falls back to plain &lt;code&gt;throws&lt;/code&gt;, and the caller has to cast the error back with &lt;code&gt;as? any AppError&lt;/code&gt;. An enum doesn’t have this problem, since all its cases share one type.&lt;/p&gt;
&lt;p&gt;Where typed throws really helps is generic code. A function that takes a closure can declare &lt;code&gt;throws(E)&lt;/code&gt; and pass on the closure’s error with its exact type, the same way &lt;code&gt;Result&amp;#x3C;Success, Failure&gt;&lt;/code&gt; does. That works equally well with enums and structs.&lt;/p&gt;
&lt;h2 id=&quot;where-do-enums-shine&quot;&gt;Where do enums shine?&lt;/h2&gt;
&lt;p&gt;While we’ve discussed the pitfalls of enums, it’s important to note that they’re not inherently bad. Enums excel in representing a well-defined set of related values, ideally non-public and on a small scale. They can be incredibly useful for representing a fixed set of options, such as API data responses:&lt;/p&gt;
&lt;figure&gt;&lt;pre class=&quot;astro-code post-css-variables&quot; style=&quot;background-color:var(--sunken);color:var(--ink); overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;swift&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--c-key)&quot;&gt;enum&lt;/span&gt;&lt;span style=&quot;color:var(--c-type)&quot;&gt; APIResponse&lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--c-key)&quot;&gt;    case&lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt; success(data: Data)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--c-key)&quot;&gt;    case&lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt; failure(error: &lt;/span&gt;&lt;span style=&quot;color:var(--c-type)&quot;&gt;Error&lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;They can also be handy for tasks like filtering data:&lt;/p&gt;
&lt;figure&gt;&lt;pre class=&quot;astro-code post-css-variables&quot; style=&quot;background-color:var(--sunken);color:var(--ink); overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;swift&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--c-key)&quot;&gt;enum&lt;/span&gt;&lt;span style=&quot;color:var(--c-type)&quot;&gt; FilterOption&lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--c-key)&quot;&gt;    case&lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt; date&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--c-key)&quot;&gt;    case&lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt; name&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--c-key)&quot;&gt;    case&lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt; location&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--c-key)&quot;&gt;func&lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt; filter(by option: FilterOption) &lt;/span&gt;&lt;span style=&quot;color:var(--c-key)&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt; APIResponse {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--c-key)&quot;&gt;  let&lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt; endpoint &lt;/span&gt;&lt;span style=&quot;color:var(--c-key)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt; ProductsEndpoint(filter: option)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--c-key)&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt; apiClient.list(from: endpoint)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--ink)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;In these scenarios, enums provide a closed set of values that makes sense for the use case and adds clarity and safety to the code.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Enums turn your code into a helicopter parent. Every call site needs a &lt;code&gt;switch&lt;/code&gt; that asks the value “which case are you?” before it can do anything. The logic for a single case ends up scattered across &lt;code&gt;navigateTo(route:)&lt;/code&gt;, &lt;code&gt;shareURL&lt;/code&gt;, &lt;code&gt;description&lt;/code&gt;, and every other switch in the codebase. Add a case, and each of those switches has to change. If the enum is public, that’s a source-breaking change for every client.&lt;/p&gt;
&lt;p&gt;Protocols let you step back. The protocol defines the contract, each conforming type implements it, and the caller just calls &lt;code&gt;route.navigate()&lt;/code&gt; without knowing which type it holds. A new route is a new struct, and no existing code has to change. That’s the Open-Closed Principle at work. Small protocols like &lt;code&gt;Shareable&lt;/code&gt; also move checks from runtime to compile time: there’s no optional &lt;code&gt;shareURL&lt;/code&gt; to unwrap, because only types that can be shared conform to it.&lt;/p&gt;
&lt;p&gt;Enums still have their place, the same way a toddler next to a busy road still needs a hand to hold. Take &lt;code&gt;success&lt;/code&gt; and &lt;code&gt;failure&lt;/code&gt;: a textbook closed set, where an exhaustive &lt;code&gt;switch&lt;/code&gt; is exactly what you want. Except it almost never stays at two. Soon someone needs &lt;code&gt;.cancelled&lt;/code&gt;, because a user leaving the screen shouldn’t trigger an error alert. Then comes &lt;code&gt;.notModified&lt;/code&gt; for a cached response, and &lt;code&gt;.unauthorized&lt;/code&gt; to send the user back to login. Every new case lands in every &lt;code&gt;switch&lt;/code&gt;, and every &lt;code&gt;switch&lt;/code&gt; grows its own branch of logic. The toddler grows up, and holding hands stops working.&lt;/p&gt;
&lt;p&gt;Keep the enum only when the set is truly closed and will stay that way. Everywhere else, stop asking and start telling.&lt;/p&gt;
&lt;h2 id=&quot;references&quot;&gt;References&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=OMPfEXIlTVE&quot;&gt;Nothing is something&lt;/a&gt; - Not everyone likes Ruby, but it’s actually an easy-to-understand and very powerful OO language, so that should not stop you from watching it.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://sandimetz.com/99bottles&quot;&gt;99 Bottles of OOP - 2nd Edition&lt;/a&gt; – Her wonderful book about writing a 99-bottle poem with a well-designed piece of software step by step. No Swift, no Kotlin, only Javascript, PHP, or Ruby. Still, very good.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://thoughtbot.com/blog/tell-dont-ask&quot;&gt;Tell, don’t ask&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.swiftbysundell.com/articles/phantom-types-in-swift/&quot;&gt;Phantom types in Swift&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item></channel></rss>