Journey to Become a Librarian or a Builder: Dynamic – part 2

In the previous part, we focused on static linking and briefly discussed the differences between frameworks and libraries.
In this article, we’ll take a closer look at dynamic linking, how the dynamic linker (dyld) works, and how dynamic libraries and frameworks behave at runtime.

Dynamic Linker

During runtime, the dynamic linker loads Mach-O files into memory. Its primary responsibility is to locate and load dynamic libraries and frameworks required by the app.

To do this, dyld relies on the install name, also referred to as the IDidentification name, or install path. This name uniquely identifies a dynamic library or framework. When you link against a dynamic library, the static linker records the library’s install name inside your app’s Mach-O binary.

At runtime, dyld uses this recorded install name to locate and load the correct binary.

So what actually happens when a framework is dynamically loaded?
How does the app know where its frameworks live?

During dynamic linking, each required dynamic library or framework is loaded into the app’s virtual memory address space. Its symbols and functions are mapped into memory just like those of the main executable.

Complie-Time vs Runtime

At compile time, dynamic libraries and frameworks go through almost the same compilation steps as regular Swift code.

The key difference appears during the link phase:

  • With static linking, the compiled code is copied directly into the main executable.
  • With dynamic linking, only the reference to the module’s location is stored in the binary.

Because of this, incremental build times for dynamic frameworks are usually very fast—almost negligible.

A Common Misconception

A very common misconception about dynamic libraries and frameworks is that they are linked on demand.

They are not.


ActuallyiOS does allow dynamic linking (just like MacOS does and NextStep before it). One can simply load the framework using dlopen. This may or may not be seen as dangerous by Apple so… use it at your own peril. Or, better yet, don’t use it at all and design your application around the problem, instead of hitting it with a hammer.

2.5.2 Apps should be self-contained in their bundles, and may not read or write data outside the designated container area, nor may they download, install, or execute code which introduces or changes features or functionality of the app, including other apps.

App Store Review Guidelines

Dynamic frameworks are loaded at app launch, during the pre-main() phase, before any of your code is executed. This means that excessive use of dynamic frameworks can negatively impact cold launch time.

dyld3 architecture example (WWDC 2017)

The good staff

Compared to static frameworks, dynamic frameworks have a major advantage:
they are not merged into the main app binary.

Instead, they are copied separately into the app bundle inside the IPA. This keeps the main executable smaller.

Static frameworks can also lead to code duplication. If the same static framework is embedded in multiple targets, its compiled code ends up duplicated in the final app binary—once per target.

Dynamic frameworks avoid this problem. A dynamic framework is embedded only once in the app bundle and can be shared across multiple targets at runtime.

For example:

  • The main app embeds an Onboarding framework.
  • Another feature target also depends on Onboarding.

In this case, the framework is linked to the feature target but not embedded again. The main app remains the single embed point.

The bad staff

Dynamic frameworks come with trade-offs.

They do not benefit from the same level of optimization as static linking. For example:

  • Dead code stripping is more limited
  • Cross-module optimizations are harder
  • The dynamic linker must resolve symbols at launch

On a cold start, dyld must analyze the app’s load commands, load all required frameworks into memory, and resolve dynamic symbols. This work increases with the number and size of dynamic dependencies.

Because of this, apps that rely heavily on dynamic frameworks often have:

  • Larger IPA sizes
  • Slower launch times

The dynamic loader examines the Mach-O load commands in the executable, loads each required framework, and resolves symbol addresses to their correct locations in memory.

Each additional third-party framework adds overhead. Although dyld caches much of this work using launch closures when the app is installed, the total cost still depends on how many libraries are loaded and how large they are.

Apple discusses this in detail in the official documentation:

Reduce dependencies on external frameworks and dynamic libraries to improve launch performance.
— Xcode App Launch Time Optimization Guide

Conclusion

Dynamic frameworks provide excellent modularity, faster incremental builds, and shared code across targets—but they are not free.

They impact launch time, limit certain compiler optimizations, and increase runtime work for the dynamic linker. As with most architectural decisions, the key is balance: use dynamic frameworks where they provide real value, and prefer static linking where performance and simplicity matter most.

In the next part, we’ll look deeper into mergable 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”).


Posted

in

, ,

by