What is @frozen and why does it matter for enums?
@frozen is part of Swift’s library evolution / resilience model — the tradeoff between binary compatibility and performance for types shipped in a library with a stable ABI.
The problem: resilient types by default
When a library is built with library evolution enabled, every public type is resilient by default — clients can’t assume a fixed memory layout, because the library author might add fields/cases in a future version without breaking binary compatibility. For a resilient enum, clients cannot exhaustively switch without @unknown default.
switch error {
case .timeout: ...
case .notFound: ...
// ❌ compiler requires @unknown default here
}
What @frozen does
@frozen is the library author’s promise: this type’s shape is fixed forever. With that promise, the compiler can:
- Drop the
@unknown defaultrequirement. - Bake the concrete layout directly into client code.
- Enable more aggressive optimization/inlining.
Why this specifically matters for enums
A resilient struct mainly costs indirect field access. A resilient enum actively forces different source code — every switch needs a fallback case. Adding a new case to a @frozen enum is a binary-breaking, source-breaking change for every client that assumed exhaustiveness.
The tradeoff
| Non-frozen (resilient, default) | @frozen | |
|---|---|---|
| Can add cases/fields later? | Yes | No — permanent commitment |
Client must handle @unknown default? | Yes | No |
| Access performance | Indirect | Direct |
| Appropriate for | Types likely to evolve | Types conceptually closed forever |
Where you’d actually see/use this
- Almost never write it yourself unless authoring a library with library evolution enabled.
- The standard library uses it extensively (
Optional,Bool,Int). @unknown defaultis what you interact with as an app developer regularly.
One-liner if pressed: “@frozen is a library author’s binary-compatibility promise that a type’s shape is fixed forever, which lets the compiler drop resilient indirection and, for enums specifically, drop the @unknown default requirement; the cost is that adding a case later becomes a breaking change, so it’s reserved for types genuinely closed by design.”