# \[Pitch\] Extensible enums for non-resilient modules

**URL:** <https://forums.swift.org/t/pitch-extensible-enums-for-non-resilient-modules/77649>\
**Category:** Pitches\
**Tags:** swift\
**Created:** [February 4, 2025, 1:28pm UTC](https://forums.swift.org/t/pitch-extensible-enums-for-non-resilient-modules/77649 "2025-02-04T13:28:04Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![FranzBusch](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/franzbusch/32/19447_2.png) [@FranzBusch](https://forums.swift.org/u/FranzBusch)\
**Post date:** [February 4, 2025, 1:28pm UTC](https://forums.swift.org/t/pitch-extensible-enums-for-non-resilient-modules/77649/1 "2025-02-04T13:28:04Z")

</div>

Hey everyone,

I'm exited to bring a previous proposal for [extensible enums for non-resilient modules](https://forums.swift.org/t/extensible-enumerations-for-non-resilient-libraries/35900) from @lukasa back together with @xedin. Over the past years, enumerations in non-resilient modules have repeatedly caused problems for API evolution leading to the general guidance of avoiding enums for public APIs in Swift packages.  
The goal of the proposal is to align the behavior of enumerations in Swift between the resilient and non-resilient language dialects.

You can find the whole pitch [here](https://github.com/swiftlang/swift-evolution/pull/2679).

Looking forward to feedback and questions!

Franz

---

<div class="post-metadata">

**Author:** ![JuneBash](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/junebash/32/19323_2.png) [@JuneBash](https://forums.swift.org/u/JuneBash)\
**Post date:** [February 4, 2025, 2:06pm UTC](https://forums.swift.org/t/pitch-extensible-enums-for-non-resilient-modules/77649/2 "2025-02-04T14:06:35Z")

</div>

I'm not so keen on this, to be honest. I work in a project that's split into many modules. Any time I use an API from a resilient module with a non-frozen enum, I find it rather annoying. I'd rather just get an error when I try to recompile with the newest version. I know that is logically impossible without just crashing in the newest version, and I don't want that either, so I understand why it exists, but our project has _so many_ enums and switch statements where we've exhaustively switched over enum cases where we'd then have to add `@unknown default` if we wanted to adopt the new language mode. It could remain opt in forever, but I think that introduces a weird split in the language. I think I'd prefer the state it's in now; require it for the combo of resilient modules/non-frozen enums, leave it out everywhere else).

---

<div class="post-metadata">

**Author:** ![j-f1](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/j-f1/32/12287_2.png) [@j-f1](https://forums.swift.org/u/j-f1)\
**Post date:** [February 4, 2025, 2:44pm UTC](https://forums.swift.org/t/pitch-extensible-enums-for-non-resilient-modules/77649/3 "2025-02-04T14:44:29Z")

</div>

Will `@unknown default` be required if you use a enum from an imported resilient module, and both your module and the imported module were built with the same `SWIFT_PACKAGE_NAME`?

---

<div class="post-metadata">

**Author:** ![guscairo](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/guscairo/32/39367_2.png) [@guscairo](https://forums.swift.org/u/guscairo)\
**Post date:** [February 4, 2025, 2:55pm UTC](https://forums.swift.org/t/pitch-extensible-enums-for-non-resilient-modules/77649/4 "2025-02-04T14:55:40Z")

</div>

+1 on this, it's definitely a pain point.

@JuneBash (and I think this may answer your question as well @j-f1) If you're referring to modules within the same package, the `@unknown default` case won't be necessary. From the proposal (emphasis mine):

> Modules consuming other modules with the language feature enabled will be forced to add an `@unknown default:` case to any switch state for enumerations that are not marked with `@frozen`. **Importantly, this only applies to enums that are imported from other modules that are not in the same package. For enums inside the same modules of the declaring package switches is still required to be exhaustive and doesn't require an `@unknown default:` case.**

---

<div class="post-metadata">

**Author:** ![JuneBash](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/junebash/32/19323_2.png) [@JuneBash](https://forums.swift.org/u/JuneBash)\
**Post date:** [February 4, 2025, 3:04pm UTC](https://forums.swift.org/t/pitch-extensible-enums-for-non-resilient-modules/77649/5 "2025-02-04T15:04:14Z")

</div>

That helps a bit, but we have a package separate from our main app target. There are also 3rd party frameworks I use that vendor enums I exhaustively switch over.

---

<div class="post-metadata">

**Author:** ![georgebarnett](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/georgebarnett/32/9137_2.png) [@georgebarnett](https://forums.swift.org/u/georgebarnett)\
**Post date:** [February 4, 2025, 4:04pm UTC](https://forums.swift.org/t/pitch-extensible-enums-for-non-resilient-modules/77649/6 "2025-02-04T16:04:28Z")

</div>

+1 on this.

As a library maintainer, not being able to add `enum`s to public API which I know _might_ change in the future is very painful. We have grown to reflexively question usage of enums in our public APIs because they cannot be changed without a new major version.

As a workaround we mint [numerous](https://github.com/grpc/grpc-swift/blob/e31dd430e34cb82428751cdf7d2c014fad956796/Sources/GRPCCore/Status.swift#L113) [enum-like](https://github.com/grpc/grpc-swift/blob/e31dd430e34cb82428751cdf7d2c014fad956796/Sources/GRPCCore/Configuration/MethodConfig.swift#L148) [types](https://github.com/grpc/grpc-swift/blob/e31dd430e34cb82428751cdf7d2c014fad956796/Sources/GRPCCore/Call/ConditionalInterceptor.swift#L27) [which](https://github.com/grpc/grpc-swift/blob/e31dd430e34cb82428751cdf7d2c014fad956796/Sources/GRPCCore/Coding/CompressionAlgorithm.swift#L18) [allow](https://github.com/grpc/grpc-swift/blob/e31dd430e34cb82428751cdf7d2c014fad956796/Sources/GRPCCore/Configuration/ServiceConfig.swift#L108) [us](https://github.com/grpc/grpc-swift/blob/e31dd430e34cb82428751cdf7d2c014fad956796/Sources/GRPCCore/RuntimeError.swift#L65) [the](https://github.com/grpc/grpc-swift/blob/e31dd430e34cb82428751cdf7d2c014fad956796/Sources/GRPCCodeGen/CodeGenError.swift#L37) [API](https://github.com/grpc/grpc-swift-nio-transport/blob/11bb8674c7870f227ff7d6d98355cb66abf9e948/Sources/GRPCNIOTransportHTTP2Posix/Config%2BTLS.swift#L20) [space](https://github.com/grpc/grpc-swift-nio-transport/blob/11bb8674c7870f227ff7d6d98355cb66abf9e948/Sources/GRPCNIOTransportHTTP2Posix/Config%2BTLS.swift#L172) [to](https://github.com/grpc/grpc-swift-nio-transport/blob/11bb8674c7870f227ff7d6d98355cb66abf9e948/Sources/GRPCNIOTransportCore/TLSConfig.swift#L19) [add](https://github.com/grpc/grpc-swift-nio-transport/blob/11bb8674c7870f227ff7d6d98355cb66abf9e948/Sources/GRPCNIOTransportCore/TLSConfig.swift#L33) [new](https://github.com/grpc/grpc-swift-nio-transport/blob/11bb8674c7870f227ff7d6d98355cb66abf9e948/Sources/GRPCNIOTransportCore/TLSConfig.swift#L62) [cases](https://github.com/grpc/grpc-swift-nio-transport/blob/11bb8674c7870f227ff7d6d98355cb66abf9e948/Sources/GRPCNIOTransportCore/TLSConfig.swift#L93) [but](https://github.com/grpc/grpc-swift-nio-transport/blob/11bb8674c7870f227ff7d6d98355cb66abf9e948/Sources/GRPCNIOTransportCore/TLSConfig.swift#L116) [are](https://github.com/grpc/grpc-swift-nio-transport/blob/11bb8674c7870f227ff7d6d98355cb66abf9e948/Sources/GRPCNIOTransportHTTP2TransportServices/Config%2BTLS.swift#L26)† worse for users to interact with. This change would improve the situation for library maintainers and users.

† there are plenty more instances like this, I just ran out of enthusiasm finding them.

---

<div class="post-metadata">

**Author:** ![dimi](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/dimi/32/20401_2.png) [@dimi](https://forums.swift.org/u/dimi)\
**Post date:** [February 4, 2025, 4:59pm UTC](https://forums.swift.org/t/pitch-extensible-enums-for-non-resilient-modules/77649/7 "2025-02-04T16:59:13Z")

</div>

I did not read past the headline, and I want to say a thousand times yes! Enums are essentially useless in any open source package without being able to mark them open in some form, which is a shame as they are one of Swift's most powerful features.

So +1000 from me.

(Ok, I'll go read the pitch now…)

---

<div class="post-metadata">

**Author:** ![0xTim](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/0xtim/32/27968_2.png) [@0xTim](https://forums.swift.org/u/0xTim)\
**Post date:** [February 4, 2025, 6:04pm UTC](https://forums.swift.org/t/pitch-extensible-enums-for-non-resilient-modules/77649/8 "2025-02-04T18:04:46Z")

</div>

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++1

I hope that conveys how much of an issue this has been up to this point and how desperately this is needed

---

<div class="post-metadata">

**Author:** ![Pippin](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/pippin/32/24826_2.png) [@Pippin](https://forums.swift.org/u/Pippin)\
**Post date:** [February 4, 2025, 6:16pm UTC](https://forums.swift.org/t/pitch-extensible-enums-for-non-resilient-modules/77649/9 "2025-02-04T18:16:42Z")

</div>

+1

I used to contribute to and still use an OpenAPI SDK generator called [CreateAPI](https://github.com/CreateAPI/CreateAPI). The schema that I personally generate from are quite large and many additions are made across many API surfaces, so when new cases are added to `enum`s I may not have the availability to update _all_ of the new cases where exhaustivity was previously used. Or there are some contexts where, as a client, the overall work for properly supporting the new case is out of the immediate scope.

Knowing that the server can change faster than a free-time-contributor-based client, the workaround has always been to have an optional initializer for unknown cases, which isn't the best.

I think this is wholly beneficial to packages since they can't benefit from library evolution mode.

Although, I'm wary how enabling the language feature is properly communicated. I've personally been on a "reduce implicitness for clarity reasons" run lately, so I do enjoy the `@extensible` attribute.

---

<div class="post-metadata">

**Author:** ![tshortli](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/tshortli/32/15850_2.png) [@tshortli](https://forums.swift.org/u/tshortli)\
**Post date:** [February 4, 2025, 6:30pm UTC](https://forums.swift.org/t/pitch-extensible-enums-for-non-resilient-modules/77649/10 "2025-02-04T18:30:31Z")

</div>

Thanks for working on this. I think that the future direction of turning on `ExtensibleEnums` by default in a future language mode is important and I hope we consider actually making that the plan of record, rather than leaving that detail to a future proposal.

The difference in behavior for clients when the same library module is built with and without library evolution has been a constant source of annoyance in the compiler codebase. Developers will often iterate on Swift libraries that are part of the compiler by building those libraries in isolation as packages, without library evolution. They don't realize then that switches in downstream code that use new enums will produce warnings when that same library is rebuilt with library evolution enabled for the real compiler build on ABI stable platforms. I'm really looking forward to being able to make the diagnostic behavior consistent and avoid this.

---

<div class="post-metadata">

**Author:** ![xwu](https://avatars.discourse-cdn.com/v4/letter/x/b487fb/32.png) [@xwu](https://forums.swift.org/u/xwu)\
**Post date:** [February 4, 2025, 6:36pm UTC](https://forums.swift.org/t/pitch-extensible-enums-for-non-resilient-modules/77649/11 "2025-02-04T18:36:45Z")

</div>

> [@FranzBusch](#):
>
> I'm exited to bring a previous proposal for [extensible enums for non-resilient modules](https://forums.swift.org/t/extensible-enumerations-for-non-resilient-libraries/35900) from @lukasa back together with @xedin.

Can you provide a moral equivalent of a "diff," so to speak, to highlight what has changed from prior pitches on the subject? Was there an obvious defect in prior iterations of this idea that has been amended? Was there engineering work that blocked prior implementation which has been unblocked?

I think you're going to get overwhelming support for not resolving the language dialect issue, but the hard part is in the details of how it's going to be implemented and staged in. It would be helpful, therefore, to understand what ended up being the barrier to taking this up over the past _many_ years we've known about and wanted to solve the problem, and what has changed now to make this pitch viable.

---

<div class="post-metadata">

**Author:** ![MahdiBM](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/mahdibm/32/45271_2.png) [@MahdiBM](https://forums.swift.org/u/MahdiBM)\
**Post date:** [February 4, 2025, 6:44pm UTC](https://forums.swift.org/t/pitch-extensible-enums-for-non-resilient-modules/77649/12 "2025-02-04T18:44:37Z")

</div>

As a package maintainer: unquestionable +9999999999999999999999999.

Optimally with some help from compiler magics, these extensible enums would also be able to handle unknown cases when decoding through Codable.  
Gets complicated but that would be ideal.

---

<div class="post-metadata">

**Author:** ![dimi](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/dimi/32/20401_2.png) [@dimi](https://forums.swift.org/u/dimi)\
**Post date:** [February 4, 2025, 6:50pm UTC](https://forums.swift.org/t/pitch-extensible-enums-for-non-resilient-modules/77649/13 "2025-02-04T18:50:22Z")

</div>

Having finally read the pitch, I'm in favor of the proposed solution.

Assuming it would be enabled by default in a future language mode though, I wonder if we would want a counterpart language feature flag to disable it by default, so that packages that previously declared a package manifest version of `6.0` could increment it to `6.x` (where x is the hypothetical future version), and disable the default explicitly to maintain source compatibility. This would allow new packages made on 6.x or later to be able to leave out the `ExtensibleEnums` flag.

Finally, `@unknown default:` would work well for switch cases, but now that we have typed errors, the final `catch` case will now be mandatory if enums are used as errors. We haven't really needed an `@unknown catch` yet up until now because most people haven't used enums in this context, but it may be worth accounting for at this time.

---

<div class="post-metadata">

**Author:** ![dimi](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/dimi/32/20401_2.png) [@dimi](https://forums.swift.org/u/dimi)\
**Post date:** [February 4, 2025, 7:00pm UTC](https://forums.swift.org/t/pitch-extensible-enums-for-non-resilient-modules/77649/14 "2025-02-04T19:00:00Z")

</div>

> [@MahdiBM](#):
>
> Optimally with some help from compiler magics, these extensible enums would also be able to handle unknown cases when decoding through Codable.  
> Gets complicated but that would be ideal.

I don't think I'd be in favor of this for decoding at least — if the goal is for a client to refer to a package that has shared types with a server, and is delivered a potentially new case (one the client was not compiled to include) it basically means that decoding would never fail? Worse, if the value were successfully "decoded" into a type, how would a custom description handle it? It's not like the module itself would be expecting this new case if it were compiled with an older version. If the client were compiled with a newer version of the library, it would be handled just fine, so I'm not sure we need additional functionality here.

---

<div class="post-metadata">

**Author:** ![8675309](https://avatars.discourse-cdn.com/v4/letter/8/ea5d25/32.png) [@8675309](https://forums.swift.org/u/8675309)\
**Post date:** [February 4, 2025, 8:09pm UTC](https://forums.swift.org/t/pitch-extensible-enums-for-non-resilient-modules/77649/15 "2025-02-04T20:09:00Z")

</div>

I feel that extensible enums should be opt-in with `@extensible`, and not-opt out (assuming eventually there is a Swift language version that has `ExtensibleEnums` always turned on).

This pitch is focused on open source modules, but in apps that are broken into modules, frozen enums are both a core safety feature and way to prevent bugs when new cases are introduced.

---

<div class="post-metadata">

**Author:** ![8675309](https://avatars.discourse-cdn.com/v4/letter/8/ea5d25/32.png) [@8675309](https://forums.swift.org/u/8675309)\
**Post date:** [February 4, 2025, 8:17pm UTC](https://forums.swift.org/t/pitch-extensible-enums-for-non-resilient-modules/77649/16 "2025-02-04T20:17:56Z")

</div>

I think library authors don't discuss the perils of `@unknown default` enough, and I don't think it's a pattern that should be encouraged in non-resilient modules.

`@unknown default` puts enum consumers in a really difficult situation: there often is no way to write code that handles any of infinite possible new cases. It ends up as something like a log statement without actually invoking product code, forcing other parts of the codebase to now be optional where it shouldn't. It really shouldn't be a solution to anything but backwards compatibility for closed-source iOS releases.

---

<div class="post-metadata">

**Author:** ![MahdiBM](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/mahdibm/32/45271_2.png) [@MahdiBM](https://forums.swift.org/u/MahdiBM)\
**Post date:** [February 4, 2025, 8:45pm UTC](https://forums.swift.org/t/pitch-extensible-enums-for-non-resilient-modules/77649/17 "2025-02-04T20:45:39Z")

</div>

> [@8675309](#):
>
> in apps that are broken into modules, frozen enums are both a core safety feature and way to prevent bugs when new cases are introduced.

The proposal mentions:

> Importantly, this only applies to enums that are imported from other modules **that are not in the same package**. For enums inside the same modules of the declaring package switches are still required to be exhaustive and don't require an @unknown default: case.

So the behavior should stay the same for you.

> [@8675309](#):
>
> I think library authors don't discuss the perils of `@unknown default` enough, and I don't think it's a pattern that should be encouraged in non-resilient modules.

Most of the times it's not an option for a package trying to follow Semantic Versioning 2.0 which is the standard nowadays.  
If your enum is an usual enum which has a chance to need to have a new case in the future, you usually have to use "Fake" enums like discussed in the proposal.

My experience from the first party and third party open source packages is similar to Franz's: 99% of the time you want extensible enums (and yes, i think 99% is an accurate estimate. If anything it might be an underestimate).

What this proposal proposes is also the safer way. If you happen to forget to mark your enum as frozen, you can just do it in a semver-patch release.  
But if you switch the default behavior, you cannot mark an enum @extensible without a semver-major release.

If you really want to have errors instead of warnings, I think you can use the other new feature that I saw was being discussed which enables promoting specific warnings to errors (and vise-versa) that you can use in CI.

---

<div class="post-metadata">

**Author:** ![michelf](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/michelf/32/1180_2.png) [@michelf](https://forums.swift.org/u/michelf)\
**Post date:** [February 4, 2025, 9:50pm UTC](https://forums.swift.org/t/pitch-extensible-enums-for-non-resilient-modules/77649/18 "2025-02-04T21:50:48Z")

</div>

For clarity, I'd use the term _non-frozen_ rather than _extensible_. This way it's immediately clear how the two terms are related and that you're just flipping the default between the two with the feature flag.

- `NonFrozenEnums` feature flag.
- `@nonfrozen` attribute.
- "Non-Frozen Enums for non-resilient modules" proposal name.

---

<div class="post-metadata">

**Author:** ![8675309](https://avatars.discourse-cdn.com/v4/letter/8/ea5d25/32.png) [@8675309](https://forums.swift.org/u/8675309)\
**Post date:** [February 4, 2025, 10:07pm UTC](https://forums.swift.org/t/pitch-extensible-enums-for-non-resilient-modules/77649/19 "2025-02-04T22:07:28Z")

</div>

> [@MahdiBM](#):
>
> > Importantly, this only applies to enums that are imported from other modules **that are not in the same package**. For enums inside the same modules of the declaring package switches are still required to be exhaustive and don't require an @unknown default: case.
> 
> So the behavior should stay the same for you.

Large apps are not built with everything defined the same package, however. Often each _team_ in an org has its own package.

---

<div class="post-metadata">

**Author:** ![8675309](https://avatars.discourse-cdn.com/v4/letter/8/ea5d25/32.png) [@8675309](https://forums.swift.org/u/8675309)\
**Post date:** [February 4, 2025, 10:13pm UTC](https://forums.swift.org/t/pitch-extensible-enums-for-non-resilient-modules/77649/20 "2025-02-04T22:13:40Z")

</div>

> [@MahdiBM](#):
>
> My experience from the first party and third party open source packages is similar to Franz's: 99% of the time you want extensible enums (and yes, i think 99% is an accurate estimate. If anything it might be an underestimate).

If it's open source, then why is it a problem for consumers to update their switch cases when a new case is added? These packages are typically shipped bundled with the app.

> [@MahdiBM](#):
>
> But if you switch the default behavior, you cannot mark an enum @extensible without a semver-major release.

Why is a semver-major release something to be avoided?

[Next page](https://forums.swift.org/t/pitch-extensible-enums-for-non-resilient-modules/77649.md?page=2)
