If you’ve followed this iOS architecture series this far, the options are all on the table: MVC, MVVM, MVP, VIPER, Clean Architecture, MV, and TCA.
But the hardest question remains. “So which one should we use for our app?”
“There’s no right answer, only trade-offs” is true, but that alone doesn’t help you decide. In this final article, let’s organize concrete criteria for making the decision.
Criterion 1: Team size — architecture is a people problem
First, match the architecture’s weight to the number of people, not the amount of code.
A team of 1–2 should prioritize speed over structural uniformity. For SwiftUI, MV (Model–View) plus a Repository is enough; with UIKit, separating navigation and networking from MVC is sufficient. At this scale, introducing VIPER (View, Interactor, Presenter, Entity, Router) or TCA (The Composable Architecture) can make structural maintenance consume feature-development time.
With 3–10 people, the team needs agreement on “where logic belongs.” MVVM + UseCase/Repository is the safe standard here. Consistent structure across screens reduces code-review and onboarding costs.
For 10+ people across multiple squads, uniformity and module boundaries come first. This is where teams choose feature modules with Clean Architecture layers, or standardize on TCA when state complexity is high. Organizations of this size were also the ones that adopted VIPER and RIBs.
Criterion 2: App lifespan — invest in boundaries for long-lived apps
Building layers for a short-lived app (a prototype, validation MVP (Minimum Viable Product), or event app) is wasteful. The goal is to build quickly and learn quickly.
For a product expected to live for 3+ years, the story changes. Server APIs will evolve, designs will be replaced, and framework migrations such as UIKit→SwiftUI will happen. What pays off here isn’t a flashy pattern but boundaries—for example, a Repository that hides data sources and a UseCase that contains business rules. Boundaries enable partial replacement; without them, you need a full rewrite.
Criterion 3: State complexity — the only axis that justifies TCA
If an app mostly “fetches data from a server, displays it, and sends input back,” MVVM/MV is enough. But when multiple screens edit the same state simultaneously, or real-time sync, offline merging, and complex undo are intertwined, TCA’s cost of enforcing controlled state-change paths starts to be justified.
As discussed in the previous article, TCA isn’t the wrong choice; it’s an expensive one. If complexity on this axis is low, you can’t recover that cost.
In one page
| Situation | Recommendation |
|---|---|
| Solo · Prototype | MV (SwiftUI) or MVC + minimal separation |
| Small team · General service app | MVVM + Repository (UseCase if needed) |
| Legacy UIKit · Binding adoption is burdensome | MVP + Coordinator |
| Large organization · Long-lived product | Clean Architecture layers + feature-based modularization |
| State-heavy domain | TCA (assuming team investment in learning) |
More important than the table is this: wherever you start, you can move to a neighboring option later. But that requires one condition.
Invariants to preserve regardless of your choice
The principle running through the entire series can be condensed to this:
Keep the View from touching the network or database directly, and keep business rules outside screen code.
If this invariant holds, moving from MVC to MVVM or MVVM to TCA becomes a “screen-layer replacement” problem. If it fails, adopting any fashionable architecture leads to a full rewrite. Architecture names stay on résumés, but boundaries are what keep products alive.
Finally, architecture migrations should follow the standard approach: start incrementally with new screens, rather than making a big-bang change. Rebuilding working screens just because a pattern is trendy usually ends in regret.
Wrapping up the series
Here’s the eight-part series condensed into one sentence per article.
- MVC: Understanding how responsibility accumulates in UIViewController is the starting point
- MVVM: Extract screen state and synchronize it through binding; without binding, it’s only half an implementation
- MVP vs MVVM: Whether the intermediary object knows the View
- VIPER: Separation taken to the extreme; its legacy lives on in Coordinator and UseCase
- Clean Architecture: Dependencies point inward only; start with Repository
- TCA: Control the path of state changes when complexity justifies the cost
- The MV debate: The essence is where logic lives and which way dependencies point, not the name
- The selection guide: Choose by team size, app lifespan, and state complexity; preserve invariants and migrate incrementally
Architecture is a tool, not a destination. Invest in boundaries so you can switch as your team and app grow.

![Cover image for [iOS Architecture #8] Choosing an iOS Architecture](/assets/images/posts/3dfe56b5-14ec-4e0a-846d-2d128e15798e/1.jpg)