Lead Essentials: Main Composition Module finished (White Belt 4th stripe)
Over the past months, I’ve slowly but steadily worked my way through the Lead Essentials course. With this module, I’ve now reached the White Belt with its fourth stripe – and it really felt like one of those “things finally click” moments.
This time, the focus was on Main Composition. If the earlier modules taught me how to think about architecture and testing, this one showed me how to actually plug everything together in a way that stays clean, testable, and ready for production.
A Better Way to Compose Apps
Before this module, I already tried to keep my projects somewhat organized, but the “composition layer” was never a clearly defined concept in my head. Here, it became obvious why it matters so much.
The idea is simple but powerful: have a dedicated composition layer that is the only place where objects are created and wired together. View controllers, presenters, view models, services, and gateways all get assembled there – not spread across random initializers or factory methods in the app.
Another big shift for me was treating Xcode modules, frameworks, Swift packages, and even directories as composable building blocks. Having used this kind of modularization in my C# projects before, it now feels great that I have learned to use that mindset in Swift and iOS development as well.
Patterns That Make a Difference
The course also explores several design patterns, but always in a practical, “use it when it solves a real problem” kind of way. I revisited the Strategy and Composite patterns, but this time with a stronger focus on how to integrate them cleanly into Swift codebases.
One highlight was applying the Composition Root pattern. It’s a subtle but powerful idea: centralize all wiring of dependencies in one place, and keep the rest of the app clean. Combined with memory- and thread-management techniques for the composition layer, it becomes much easier to prevent common issues like leaks or inconsistent execution contexts.
Testing From Every Angle
As with the other modules, testing wasn’t an afterthought – it was baked into everything. The goal stayed the same: reduce complexity, speed up feedback, and increase confidence.
UI testing
UI tests suddenly became a lot less flaky once I learned how to take full control over app and network state. Using launch arguments and conditional compilation to put the app into predictable scenarios felt like finally getting a grip on something that used to be more “hope than strategy”.
Acceptance testing
One of the biggest mindset shifts for me was letting go of slow, end-to-end UI acceptance tests and replacing them with fast integration acceptance tests instead. At first, this felt a bit wrong – I was so used to thinking “end-to-end or nothing”. But after seeing how fast, stable, and focused these integration tests can be, I’m fully convinced.
Snapshot testing
Snapshot testing also leveled up. Instead of running the full app and hoping for consistent screenshots, the course showed how to render views in isolation and drive them directly. That alone makes snapshot tests much faster and less brittle.
Dark Mode support was a good reminder as well. I tend to push it late in the process, and this module showed how easy it is to include it from the beginning when you have the right test setup.
“Impossible” tests
I also enjoyed the part about testing things that feel “impossible” to test: private methods, system classes, and behavior without nice public entry points. With the right architecture and a few Swift techniques, a lot of this becomes surprisingly manageable.
Effective Use of Combine
I’ve never used Combine before this course, so this was my first deep dive into it. The course did a great job of showing how Combine can simplify communication between components in a testable way. Instead of sprinkling custom callbacks, delegates, and completion handlers all over the place, you can lean on Combine to unify those patterns and remove duplication. Seeing how the same abstraction can sit underneath different architectural choices was a real eye-opener for me.
CI, CD and App Store Delivery
The lessons didn’t stop at the codebase itself. This module walks through modern CI/CD practices and shows how to integrate continuous delivery and deployment into a real iOS workflow. From local builds to pipelines that automatically ship a build to App Store Connect, everything is covered end-to-end.
Even though I already use GitHub Actions heavily, the course thaught me new ways of doing CI, CD and deployment tasks and fitting them into a professional setup.
Simulating the Full App Lifecycle
A recurring theme in this module is understanding an app’s lifecycle — not from the perspective of the user, but from the perspective of tests. Learning how to simulate app launches, state transitions and even rare scenarios made these scenarios a lot clearer. These techniques will definitely influence my future projects, including the SwiftUI rewrite of TwistReader.
Wrapping up
This module was dense, no doubt about it. But it also pulled together a lot of threads from the earlier parts of the course: architecture, composition, modularization, testing, and delivery.
Reaching this White Belt level feels like more than just unlocking the next badge. It feels like I’ve gained a much more practical, reality-proof way of thinking about how to build scalable and testable Swift apps.
And of course, I’m already curious about what the remaining modules until the blue belt, which will be rewarded for completion of the course, will bring.
Click to see the full iOS Lead Essentials curriculum
Disclaimer: This blog post was written with the help of AI, based on a bulleted summary of learning topics provided as part of the Lead Essentials program. The structured list served as the foundation for turning the content into a more readable, narrative-style post that reflects my personal learning experience. I reviewed and edited the post before publishing to ensure it meets the quality standards of this blog.
