Swift & Objective-C

[Advanced Swift #9] Swift ABI Stability: Why App Sizes Shrank in 2019

App sizes shrank after Swift 5 and iOS 12.2 shipped in 2019, thanks to ABI stability. This article covers how the Swift runtime moved from every app into the OS, along with module stability and the trade-offs of @frozen.

6 min read
Cover image for [Advanced Swift #9] Swift ABI Stability: Why App Sizes Shrank in 2019

In spring 2019, something strange happened when iOS 12.2 shipped with Swift 5. App sizes dropped by several MB without any changes. Swift ABI Stability and More

This continues from the previous article Advanced Swift #8.

The secret was hidden in one dry line of the release notes: ABI stability achieved.

The final article in the Swift series tells the full story: what ABI is, why it took five years, and what it made possible.

It is where the language’s history meets its internal architecture, making it a fitting topic to close the series.

What Is ABI — A Contract Between Binaries

Everyone knows API: a contract at the source-code level—what a function is called and what its arguments are.

ABI (Application Binary Interface) is the layer below that: a contract between compiled binaries.

It covers which registers carry arguments during a call, how a struct is laid out in memory—the sizes from the layout article—and how symbol names are formed (name mangling) and metadata is located.

Why does this contract matter? Because two binaries built at different times with different compilers must work together.

That is exactly the relationship between an app and libraries built into the OS. If the ABI changed between versions, an app built with Swift 5.0 could not communicate with the Swift 5.5 runtime. Swift ABI Stability and More

That is how Swift’s early days worked. The ABI changed with every version, so each app had to carry the entire Swift runtime and standard library for the version it used.

That meant several MB of duplicated baggage in every app, and the OS could not use Swift in its own frameworks. System frameworks must also communicate with future apps, and there was no such guarantee.

Swift 5’s ABI stability declaration froze this contract. Calling conventions, type layouts, and name mangling were finalized, and later versions evolve while honoring it. Swift ABI Stability and More

The standard library immediately moved into the OS, reducing app sizes. It also opened the door for Apple to build system frameworks such as SwiftUI in Swift.

That size reduction in 2019 was a sign that the language had come of age. Swift ABI Stability and More

Why Five Years — The Weight of Freezing

To understand the question “Why not freeze it earlier?”, consider the cost of freezing. Finalizing an ABI means carrying even the design mistakes of that moment forever.

Once a layout or convention is promised, it cannot change as long as existing binaries remain in the world.

Recall how turbulent Swift 1–4 were. The syntax was overhauled with every version. Had the ABI been frozen then, today’s Swift would be trapped in its early design. Swift ABI Stability and More

Swift waited until generic implementation had stabilized and value-type layout strategies had been validated. Those five years also allowed major design debates to settle through the Evolution process.

Looking at cases where C++ rejects improvements because of decades of ABI inertia shows the value of this caution.

Before-and-after comparison of Swift runtime backpacks carried by each app moving to a shared OS pillar
The runtime backpack each app carried has moved into a shared OS pillar

The Second Contract — Module Stability and Library Evolution

But ABI stability leaves one important gap: binary framework distribution.

Before Swift 5.0, compilers exchanged module interfaces as a binary format called swiftmodule, which was tied to the compiler version. Swift ABI Stability and More

Upgrade Xcode once, and every distributed SDK became unusable.

Swift 5.1’s module stability solves this problem. Stable, text-based interface files (.swiftinterface) were introduced, allowing frameworks built with one compiler version to be read by another. Swift ABI Stability and More

The ecosystem that distributes SDKs as XCFrameworks—payment, analytics, and mapping SDKs—stands on this foundation.

The paired concept is library evolution mode. It builds a framework so that adding stored properties later will not break existing apps, but the cost is interesting.

A public struct in this mode becomes a resilient type whose layout is not finalized. Clients cannot know its size at compile time and pay the cost of indirect access.

It is the exact converse of “knowing the size makes it fast” from the layout article.

That is why evolution mode provides an escape hatch called @frozen: “Freeze this type’s layout, and allow direct access instead.” Int and Optional in the standard library are @frozen for this reason.

For app developers, the practical takeaway is simple. App targets and source-distributed packages—most SwiftPM packages—do not need this mode, and enabling it is a disadvantage.

It is an option for teams distributing SDKs as binaries. The answer to “What is BUILD_LIBRARY_FOR_DISTRIBUTION?” is in this paragraph.

Conclusion — One Story Running Through the Series

There is a reason ABI is the final topic in the Swift series: every concept covered so far converges here.

Structs cross binary boundaries because value-type layouts were frozen. Protocols became framework APIs because the witness table format was finalized.

And because the Evolution process had matured the designs, Swift was ready to freeze them.

Safety through types (optionals, ARC, Sendable), costs made explicit (unsafe, any, @unchecked), and complexity presented as steps (Progressive Disclosure).

The series’ central message is that these principles remain consistent from the syntax layer all the way down to the binary layer.

Swift is still building the next steps on the Evolution forum. I hope this series becomes a map for reading those changes independently. This concludes the series.

Illustration contrasting access speed between an evolvable framework and an @frozen structure
Flexibility through evolution or speed through freezing? @frozen is the switch

Summary

  • ABI is a contract between binaries—calling conventions, layouts, and name mangling—and was frozen in Swift 5 (2019). As a result, the standard library moved into the OS, reducing app sizes and enabling Apple to build SwiftUI in Swift.
  • Freezing took five years because it meant carrying design mistakes forever. That time was spent waiting for generics, layouts, and the Evolution process to mature.
  • Swift 5.1 module stability (.swiftinterface) opened the binary framework ecosystem built around XCFramework.
  • Library evolution mode trades indirect-access costs for apps that keep working as frameworks evolve, with @frozen as the escape hatch. It is unnecessary for app targets.
  • The series’ conclusion: Swift’s principles—typed safety, explicit costs, and staged complexity—remain consistent from syntax to binaries. Swift ABI Stability and More

Sources and Verification Criteria

  • Swift ABI Stability and More — Swift.org · Official announcement · Verified 2026-08-17 · Basis: Swift 5 ABI stability, OS runtime inclusion, and changes in app size

Continue reading

Advanced Swift Series