Xcode 16 introduces an experimental setting to explicitly build Swift modules. Here’s my notes from trying it out.
What’s The Problem?
When Xcode 15 builds projects it builds modules as the compiler comes across each import statement. This approach of implicitly built modules can block build tasks while they wait for modules they depend on to build.
Xcode 16 introduces an experimental Swift build setting, off by default, to explicitly build modules.
Explicitly Built Modules
To summarise my understanding of how it works:
- Xcode scans the project’s source files building a graph of the imported modules for all targets.
- As Xcode discovers the dependencies between modules it builds those whose dependencies are ready avoiding delays.
- This process continues until finally it builds the target using the now already built dependencies.
This explicit discovery and build process avoids the build system having to wait for unbuilt modules. Apple also claims this makes the debugger faster as it can share the already built modules with the debugger.
Build Setting
Xcode 16 uses explicitly built modules for C and Objective-C code but it’s experimental for Swift code. You need to enable it in the project settings, in the Swift Compiler - General settings (search for Explicitly Built):
If you’re using an xcconfig
file:
_EXPERIMENTAL_SWIFT_EXPLICIT_MODULES = YES
Showing the Build Timeline
I found it interesting to compare the build timeline for implicit vs explicit builds. To view the timeline, select the build log in the report navigator (⌘-9) and show the Xcode Assistant Editor (⌃⌥⌘⏎):
I changed the default layout to show the assistant view on the bottom. You can zoom the timeline in and out with a trackpad pinch gesture or using the mouse scroll-wheel while holding the ⌥ key. Clicking on a line in the build log highlights the corresponding item in the build timeline (and vice-versa).
After cleaning the build folder (⇧⌘K), this is the default, implicit build:
The timeline shows build tasks for each target in different colours. The blocks on the right-hand side show the building of the app target once the build completes for the modules it depends on.
This is how it looks after setting explicitly built modules to yes:
The difference is around the 7 second mark when the compile module tasks start.
These correspond to the top-level compile module tasks in the build log that the build system shares across targets:
Is it Faster?
I’m not sure how representative my timings are but I’m not seeing any faster builds using explicitly built modules. If anything, it’s slower than the implicit builds in my tests.
I tested using an Apple M1 Mac mini with 16GB RAM and cleaned the build folder each time. The numbers below are an average over three builds for two reasonably complex open-source projects:
Project Implicit Explicit
NetNewsWire 24.3s 29.7s
IsoWords 88.4s 95.5s
Another potential benefit of explicitly built modules is that Xcode now shares the built Swift modules with the debugger. Apple mentions that this should reduce the delay when evaluation expressions using “p” or “po”. In my so far limited experience I still see delays of several seconds so maybe this is not yet working or I’m doing something wrong?
I should add that this is still experimental for Swift and I’m testing with the second beta of Xcode 16. I’ll update this post as we get future Xcode betas if I see any significant changes.
If you try it and see more conclusive results let me know.