SwiftQA Random

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:

  1. Drop the @unknown default requirement.
  2. Bake the concrete layout directly into client code.
  3. 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?YesNo — permanent commitment
Client must handle @unknown default?YesNo
Access performanceIndirectDirect
Appropriate forTypes likely to evolveTypes 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 default is 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.”