AI Briefing
KO

A Year-Long iOS Modularization Journey

·2025.12.30 09:00

Key point

While carrying out iOS modularization throughout 2024, I learned that managing dependencies and minimizing the scope of impact matter more than simply cleaning up structure.

1 / 2

Details

Over the course of 2024, as I carried out full-scale iOS modularization, I came to realize that managing dependencies first and reducing the scope of impact matters more than simply splitting up code. At first, to move from a single project to feature modules, I created empty modules and moved files, but the coupling between code was higher than expected, making a clean separation difficult.

The initial target structure was a 3-layer setup: Core / UI / Features. The plan was to place common logic, models, and repositories in Core, reusable components and resources in UI, and independent feature-unit modules in Features. In practice, however, separating feature modules independently required first sorting out the dependency relationships between models and repositories, which led to the strategy of moving them to ZCore first.

The large-scale move carried a heavy burden in terms of stability. In particular, moving all the models to ZCore at once caused a sharp spike in the number of changed files, required adding import ZCore at every usage site, and revealed unexpected dependency issues. On the other hand, this process did clean up the structure, and separating other files afterward became much easier.

Going forward, I concluded that it would be more appropriate to proceed in smaller units.

  • Sequentially move models used only by a single feature or a specific screen first
  • For shared models, declare them redundantly in both ZCore and the app module first to minimize impact on existing code
  • Gradually clean up the duplicate models and achieve full separation in subsequent updates

There were clear regrets as well. Because module naming conventions weren't decided early on, names ended up inconsistent, like ZigZagCore, ZigZagUI, MarketingLogger, and Utils, and later, when unifying them under a Z prefix, a runtime crash occurred. The cause was that the Inspector settings of a legacy xib file remaining in the ZUI module still referenced the old module name, and this setting couldn't be caught through code search alone.

Another concern was App Launch Time. Most of the initial modules belonged to the Core Layer and inevitably had to be configured as Dynamic Frameworks, and as their number grew, launch time issues began to surface. Currently, improvements are being experimented with, such as converting some modules to static, or switching CocoaPods used as Dynamic frameworks to SPM.

As modules were split apart, code with ambiguous role boundaries also came to light. For example, a model like UserAccountInitType naturally belongs in ZCore, but extension logic like localizedName, which references string resources, could also be considered to belong to ZResource, causing module boundaries to blur. To handle such cases, the option of having a dedicated extension module is even being considered.

In the end, the lessons learned can be summarized into three points.

  • Start small and expand gradually
  • Clearly define the role and dependencies of each module
  • Always minimize the scope of impact and prioritize stability

Modularization in 2024 wasn't easy, but it was a time to learn both how to organize structure and which mistakes to avoid. Going forward, the plan is to resolve code coupling in more detail and expand modularization one step at a time.

This summary was generated automatically by AI. Check the original for the author's claims and context. Copyright belongs to the original author.

Our guide explains how the AI works. Report summary errors, attribution issues, or removal requests via Contact.