In this series, I want to share some thoughts on how static, dynamic, and mergeable libraries work in the Apple ecosystem. We’ll unpack what they are, how they differ, and why you might choose one over the other.
Before we get to that, though, there’s a term that keeps popping up when you start digging into linkers and libraries — Mach-O.
Mach-O
When I first heard “Mach-O,” I thought it had something to do with airplanes or fighter jets. Sadly, no Top Gun reference here.

Mach-O stands for Mach Object, and it’s a file format used by macOS and iOS (amongst other, less known OSes) to store executables, object code, shared libraries, dynamically loaded code, and even crash dumps.
Think of it as a container — a box that holds all the compiled stuff your app needs so the system can actually run it.
You might’ve already seen the term “Mach-O” in Xcode without realizing it. For example, under your target’s Build Settings → Linking → Mach-O Type, you’ll find several options there when u happened changing type of your library. That’s what this is about.
There’s a lot more to Mach-O, its history and its usage. It’s what allowed MacOS to easily contain multiple architectures when Apple moved from one system to another, changed 32bit to 64 bit, etc. If you want more info, you can read more on FAT libraries and the history of Mach-O. Bonus content about Mach-O symbols.
Now that we know what Mach-O means, we can move on to the next piece of the puzzle: the static linker.
Static Linker
The linker (also known as the static linker) runs at build time.
Its job? Combine a bunch of inputs — object files, static libraries, dynamic libraries, and configuration items — into a single output.
That output is usually a Mach-O binary (the final executable), but the linker can also spit out other formats, like object files or link maps (if you’ve ever debugged symbol conflicts, you’ve probably met one of those).
Apple’s linker has gone through a few generations:
- ld – the original, ancient one from the early Mac OS X days.
- ld64 – a rewrite from around 2005 that eventually became the default.
- ld_prime – introduced in Xcode 15. It’s not a separate binary, just a new implementation that you can toggle using flags (-ld_classic or -ld_new).
Libraries vs frameworks
At first glance, libraries and frameworks sound like the same thing. Spoiler: they’re not.
A library is just a bundle of reusable code — functions, classes, utilities — that your app can call. Libraries come in two flavors:
- Static (.a)
- Dynamic (.dylib)
A framework, on the other hand, is a more complete package. It can contain code and resources — like assets, images, storyboards, or nibs. Frameworks are distributed as .framework bundles.
And then there’s .xcframework, which is essentially a wrapper that can hold multiple frameworks or libraries built for different platforms (iOS, macOS, watchOS, etc.) separated by differing characteristic (target OS, target architecture etc.).
Tell me more
Now that we’ve covered the basics, let’s talk about why you might use a static framework or library.
The big one: performance.
When you build your app with a static framework, the linker takes that framework’s code and bakes it directly into your app binary. It’s not loaded dynamically at runtime — it’s already there.
That has a few benefits:
- The system doesn’t have to load the library at launch → faster startup time
- During linking, you get dead code stripping and symbol conflict resolution
- Everything is neatly packed into one binary
For example, if both your app and a linked framework define a function called getUserId, the linker will catch that and handle it (either by throwing an error or by namespacing it internally).
The bad things
Of course, there’s no free lunch. Static linking makes your app larger, since every linked framework’s code gets embedded directly into the .app bundle. You’ll notice that when exporting your .ipa — and your users will notice it when downloading your app.
There’s also a subtle but important issue with resources.
Let’s say your static framework includes asset catalogs (like icons or design system colors). If you add that framework to both your main app target and your widget, you’ll end up with duplicate asset catalogs in the final product — not great.
One thing to add about static libraries. When working on a really large applications you might hit a wall when using static libraries exclusively. There is a limit as to how big the actual executable can be. While the whole uncompressed bundle (unzipped IPA file: executable and all of your resources) can hit up to 4GB of size the actual executable can be up to 500MB (on iOS at least). Again the trick is to know when mix & match works for your use-case.
To wrap it up
In this part we learn some basis about Mach-O, what is a framework, differences between libraries and advantages and disadvantages of using static frameworks. In next part we will take a look at dynamic linking and frameworks. And then we move to mergeable libraries.

Szymon Szysz is a ex-Apple hater and has come a long way. Now making apps for iOS since 2017. Likes good design and amazing UX. Likes to focus on code quality and does not hesitate to say that something “is not yes” (*ponglish, “is not correct”).

