Library evolution mode: which types need @frozen to keep ABI stability across framework updates
0 reputation · 04 Feb 2026, 14:11 UTC
0 reputation · 04 Feb 2026, 14:11 UTC
I'm maintaining a small Swift framework consumed by an application that must be updatable without recompiling or restarting dependent clients. The framework is built with library evolution mode enabled, and I understand Swift 5's ABI stability lets the app run against a newer Swift runtime as long as the deployment target is iOS 12.2 / macOS 10.14.4 or later.
What I can't resolve is the resilience boundary for my own public types. My understanding is that structs and enums default to resilient under library evolution, and that marking them @frozen trades future flexibility for performance, while @inlinable code can silently bake implementations into client binaries and break compatibility if the implementation later changes. The unresolved part is generic types with associated-type-like constraints, where the resilience model seems less clearly specified.
My constraints: some hot-path value types would benefit from frozen layout, but I may need to add stored properties in a future release, and I cannot force clients to redeploy.
Given that, my questions are:
@frozen (or must avoid it) to guarantee binary compatibility across framework updates?otool -L and runtime testing, that a given public API surface is ABI-safe before shipping?31275 reputation · 04 Feb 2026, 20:50 UTC
To guarantee binary compatibility across framework updates in Library Evolution mode, you must avoid @frozen on any type where you anticipate adding stored properties or enum cases in the future. In Swift, no public type "requires" @frozen to be ABI-safe; rather, @frozen is an optimization that explicitly disables resilience.
@frozen): This tells the compiler the layout will never change. The client can bake the exact memory offsets into its binary for performance. If you add a property to a @frozen type in a later update, any client not recompiled against the new version will likely crash due to memory misalignment.Generic types and those with associated-type constraints follow the same resilience rules as standard structs and enums. There is no separate "manual-annotation" requirement specifically for generics to make them resilient; they are resilient by default unless marked @frozen. If a generic struct is non-frozen, the compiler handles the layout indirection regardless of the constraints applied to its generic parameters.
Beyond otool, you can verify the resilience of your public API using the following approach:
BUILD_LIBRARY_FOR_DISTRIBUTION = YES is set in your Xcode build settings.swiftc -emit-module -module-name YourFramework YourFile.swift
Search the output for the frozen keyword. If a type is not explicitly marked frozen in the emitted module, it is resilient.| Type Goal | Annotation | ABI Impact of Adding Property/Case |
|---|---|---|
| Future Flexibility | None (Resilient) | Safe / Compatible |
| Maximum Performance | @frozen |
Breaking Change |
| Inlined Logic | @inlinable |
Potentially Breaking (Logic is baked in) |
Missing Detail: Are you using @inlinable methods that access stored properties of these types? If so, the resilience of the type is bypassed by the inlined code, which may require a different strategy to avoid crashes.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.