In team meetings and job postings, it is common to hear phrases like *"our app uses MVVM architecture"* or *"we migrated to VIPER architecture"*. The intent is good: communicate how the code is organized. The problem is that this vocabulary mixes two levels of abstraction that software engineering literature has kept separate for decades: patterns (localized solutions to recurring problems) and architecture (the global structure of the system, its boundaries, and trade-offs).
This article grows out of swift-patterns-studies, a Swift project (local codebase: EstudoPatterns) where the same authentication journey (login and sign-up) is implemented four times, with MVVM, MVVM-C, VIP, and TCA, sharing domain, networking, and visual components. If these were four incompatible "architectures," each one would require rewriting half the app. That is not what happens: only the presentation layer changes. That is not a minor technical detail; it is the central clue for understanding why we should call these names patterns, not architecture.
By the end, as I usually do in my posts, you will have practical criteria for classifying decisions in your next iOS project and bibliographic references to go deeper.
This post was also written in Portuguese, click here to access the Portuguese version.
What is a pattern?
The most cited definition comes from the *Gang of Four* (Gamma, Helm, Johnson, and Vlissides, 1994): a pattern describes a reusable solution to a problem that occurs repeatedly in a given context. It is not a snippet of code to copy and paste, nor a complete framework. It is shared vocabulary: names, responsibilities, and consequences.
Buschmann et al. (1996), in the first volume of *Pattern-Oriented Software Architecture* (POSA), refine this idea at different scales:
- Design patterns: organize classes and objects within a subsystem (small scale).
- Architectural patterns: describe the high-level structure of an entire system (for example, layers, microkernels, pipes-and-filters).
MVVM often appears in conversation as an "architectural pattern" in some materials. In iOS development practice, however, it almost always operates in the interface layer: how a screen holds state, reacts to taps, and talks to services. It does not define where repositories live, how modules import each other, or how the app is deployed.
Martin Fowler (2002), in *Patterns of Enterprise Application Architecture*, documents the Presentation Model, the direct conceptual predecessor of MVVM: an object that holds the state needed to render the view, so the view stays as "dumb" as possible. Fowler makes it explicit that the scope is presentation, not the system as a whole.
Traits that help you recognize a pattern
- Limited scope: answers a local question: "How do I organize state and events for this screen or flow?"
- Substitutability: can be swapped without bringing down the rest of the system, as long as external boundaries are respected.
- Composition: multiple patterns coexist (MVVM + Coordinator is the classic example).
- Infrastructure independence: in theory, the same presentation pattern works with REST, GraphQL, or mocked data, as long as the data architecture is well defined elsewhere.
A pattern guides; it does not govern the entire app.
A quick example outside iOS
Think of the Strategy pattern: you encapsulate interchangeable algorithms (for example, different discount policies) behind the same interface. Changing the strategy does not redefine the e-commerce site's architecture; it redefines one point of variation. The same reasoning applies to MVVM: changing how a screen exposes state does not redefine where business rules live, unless you put business logic inside the ViewModel (which would be a symptom of a poorly drawn boundary, not praise for the pattern).
What is software architecture?
If a pattern is local, architecture is global. Bass, Clements, and Kazman (2012), in *Software Architecture in Practice*, define architecture as the set of structures of the system (computational elements, relationships between them, and properties of both), chosen to satisfy functional requirements and, above all, quality requirements (maintainability, testability, scalability, security, evolution by teams).
Robert C. Martin (2017), in *Clean Architecture*, summarizes a rule that became an industry reference: dependencies point inward. The domain (business rules) does not know about UI, databases, or frameworks. Presentation and infrastructure are replaceable details plugged into stable ports (interfaces).
Philippe Kruchten (1995), with the 4+1 model, reminds us that "architecture" is not a single diagram: there are logical, process, development, physical, and scenario views. A UI pattern is, at most, a slice of the logical view, never the full map.
A practical checklist for iOS developers
Before calling something "app architecture," ask:
- Does it define boundaries between modules and the direction of dependencies across the whole project? If not, it is probably a pattern or folder convention.
- Does it explicitly cover domain, data, infrastructure, and presentation? If not, it is a single-layer decision.
- Does it remain valid if you swap SwiftUI for UIKit, or REST for another transport? If not, it is coupled to UI or networking technology.
- Does it ground trade-offs around teams, deploy, integration tests, and evolution? If not, it is local code organization.
If the change only reorganizes ViewModels, Coordinators, or Reducers, without changing where the domain lives or how packages relate, you are choosing a presentation pattern, not redesigning system architecture.
Architecture is not synonymous with "many folders"
It is tempting to believe that creating `Domain`, `Data`, and `Presentation` directories automatically produces architecture. Folders are development views (Kruchten); they only become architecture when dependencies compile in the right direction. A VIP `Interactor` that calls `URLSession` directly breaks the same rule as a bloated ViewModel: a weak boundary between presentation and infrastructure. The pattern name does not save an inverted import graph.
In EstudoPatterns, the proof is in the compiler: presentation modules import `Shared` and `DesignSystem`, never the other way around. That is a small but real architectural decision, more real than labeling the app "MVVM" in the README.
Two scales, one app
The diagram below contrasts what is structural (architecture in the broad sense) with what is a study variant (patterns) in EstudoPatterns:

The dividing line is not dogmatic: Shared and DesignSystem are decisions that approach architecture because they stabilize the system while UI patterns change. MVVM, MVVM-C, VIP, and TCA are pedagogical plug-ins on the same power outlet.
Why EstudoPatterns patterns are not architectures
The project implements authentication against the dev-challenge API. In every presentation module:
- Domain (`AuthCredentials`, `RegistrationForm`, `AuthToken`) lives in `Modules/Shared`.
- Networking (`AuthRepository`, `AuthAPI`, MLNetworkLayer) is also in `Shared`.
- Reusable UI (`DSTextField`, `AuthStrings`, `AuthSuccessView`) lives in `Modules/DesignSystem`.
The app hub (`PatternHubView`) only chooses which presentation module to open. Each item describes how the flow is organized, not what the system does. See the subtitles in `PatternItem.swift`:
- MVVM: *"ViewModel holds state; navigation in the View."*
- MVVM-C: *"ViewModel emits events; Coordinator decides routes."*
- VIP: *"View, Interactor, Presenter; Router navigates."*
- TCA: *"Reducer + Store; unidirectional flow with effects."*
These are differences in interface orchestration, not business logic.
Analysis by pattern
MVVM
- What it solves: binding between View and state; ViewModel triggers presentation effects.
- What it does not define: where repositories live, networking policy, boundaries between modules.
MVVM-C
- What it solves: MVVM + Coordinator, navigation pattern that decouples screen transitions.
- What it does not define: SPM package structure, domain, CI pipeline.
VIP (Clean Swift)
- What it solves: View, Interactor, Presenter, and Router roles within a flow.
- What it does not define: global layers; the Interactor here is not, by itself, the Use Case layer of Clean Architecture for the whole app.
TCA
- What it solves: unidirectional flow with `Action`, `Reducer`, `Effect`, and new state (Point-Free, 2020+).
- What it does not define: infrastructure; it is a library + UI state management pattern.
The substitutability argument
In EstudoPatterns, swapping the MVVM module for TCA does not require rewriting `AuthRepository`, domain models, or DesignSystem components. `Shared` tests remain valid; only presentation-layer tests change (ViewModel, Coordinator, Presenter, or `TestStore`).
If these were mutually exclusive architectures in the strong sense, that swap would be a *rewrite*. It is a swap of UI adapter, exactly the behavior you expect when the base architecture is well separated from presentation patterns.
Comparable flow map

Notice: AuthRepository (or its client) appears in every flow. The pattern shapes the path between the View and the repository; the data architecture stays the same.
Common confusions in the iOS community
"Our app uses MVVM architecture"
Apple documents MVC as the Cocoa layer pattern: models, views, and controllers with roles defined in the ecosystem (Apple Developer Documentation). MVVM emerged in the community as an adaptation of Fowler's Presentation Model for declarative binding (SwiftUI, Combine, RxSwift). It is a widely adopted presentation convention, not a normative system architecture document from the platform.
"VIPER is our bank / startup architecture"
VIPER (and variants like Sutherland's VIP/Clean Swift) impose strict separation per feature: View, Interactor, Presenter, Entity, Router. That improves isolation in large UIKit codebases, but in real apps VIPER coexists with Coordinators, workers, SPM modules, and shared domain layers. VIPER is a modular pattern per screen or flow, not a single map of the app, especially when each squad names folders differently.
"We adopted TCA as architecture"
Point-Free's Composable Architecture is a library that implements a unidirectional state pattern inspired by Redux and Elm. Excellent for testing complex flows (`TestStore`), but mature TCA apps still separate Domain and Data; the reducer does not replace repositories or central business rules. TCA is pattern + tool, not an automatic substitute for Clean Architecture.
"Coordinator is an architecture"
The Coordinator (popularized by Soroush Khanlou and widely documented in the Swift community) solves navigation: who pushes which screen, with which dependencies, without coupling view controllers or views to each other. Pattern selection matrices, including those used in EstudoPatterns, usually classify it in the navigation layer, not as complete architecture. MVVM-C in the project is exactly that: MVVM plus a navigation pattern.
Why does the confusion persist?
Three cultural forces explain the habit of saying "architecture" when we mean "pattern":
- Marketing and job posts: "experience with X architecture" fits a résumé line better than "experience with presentation pattern X."
- Bootcamps and tutorials: many teach MVVM as if it were the entire skeleton of the app, because it is pedagogical to start from the screen.
- POSA's movable boundary: some presentation patterns are described as "architectural" in books, which is technically defensible at the scale of a UI subsystem, but becomes noise when we treat them as substitutes for domain and data.
Recognizing these forces is not academic purism: it is a tool for estimating refactors. Migrating from MVVM to TCA in EstudoPatterns is work across a few files in `Modules/TCA`. Migrating from a coupled monolith to modules with an isolated domain is architecture work, weeks or months, depending on legacy size.
When it makes sense to talk about architecture in your app
Returning to EstudoPatterns, these decisions are already architectural in the useful sense of the term:
- Modularization via Swift Package Manager: `Shared`, `DesignSystem`, and one module per presentation pattern. This defines dependency graphs, compile times, and team ownership.
- Domain/data boundary: `AuthRepositoryProtocol` in Shared, implementation with MLNetworkLayer, `Sendable` DTOs, domain errors (`AuthError`). Presentation depends on a stable contract, not HTTP details.
- Shared design system: common tokens, strings, and components ensure a fair comparison between patterns and prevent visual identity from fragmenting.
- Single entry hub: the app shell is not "MVVM"; it selects which pattern to study. The study product's architecture is "common base + pluggable variants."
Next steps that would strengthen architecture (without changing the patterns):
- Explicit Use Cases in the domain layer (`LoginUser`, `RegisterUser`).
- Session persistence (Keychain) behind a port in Shared.
- Modules per business journey (`Statement`, `Balance`) using the same base.
Naming suggestions
- Avoid: "MVVM architecture". Prefer: "MVVM presentation pattern".
- Avoid: "Let's use VIPER in the app". Prefer: "VIP pattern per feature in the UI layer".
- Avoid: "The app is TCA". Prefer: "The UI uses TCA; domain and data live in…".
- Avoid: "Which architecture is better?". Prefer: "Which presentation pattern fits this flow and this team?".
Language precision reduces false debates. MVVM and TCA do not compete as "rival architectures" if both consume the same `AuthRepository`.
Conclusion
Patterns and architecture are not enemies: they operate at different scales. Patterns address recurring problems in a bounded context. Architecture organizes the whole system: modules, dependencies, domain, infrastructure, and qualities you cannot glue on later with a ViewModel refactor.
EstudoPatterns exists to make that distinction visible in code: the same authentication, the same repository, the same buttons, four ways to drive state and navigation in the interface. That does not prove MVVM is "worse" or TCA is "better." It proves they are interchangeable at the layer where they operate, as long as the architectural base is solid.
The next time someone proposes "changing the app architecture to X," it is worth asking: are we changing how the entire system is structured, or only how screens organize state and events? An honest answer saves months of unnecessary migration.
To try it in practice, clone the swift-patterns-studies repository, open the pattern hub, and compare flows side by side. The technical guide Auth comparison across patterns details who navigates, who calls the API, and how to test each module.
References
- GAMMA, E.; HELM, R.; JOHNSON, R.; VLISSIDES, J. *Design Patterns: Elements of Reusable Object-Oriented Software*. Addison-Wesley, 1994.
- BUSCHMANN, F. et al. *Pattern-Oriented Software Architecture, Volume 1: A System of Patterns*. Wiley, 1996.
- FOWLER, M. *Patterns of Enterprise Application Architecture*. Addison-Wesley, 2002. (Chapter *Presentation Model*.)
- FOWLER, M. Presentation Model. Available at: https://martinfowler.com/eaaDev/PresentationModel.html. Accessed: June 10, 2026.
- MARTIN, R. C. *Clean Architecture: A Craftsman's Guide to Software Structure and Design*. Prentice Hall, 2017.
- BASS, L.; CLEMENTS, P.; KAZMAN, R. *Software Architecture in Practice*. 3rd ed. Addison-Wesley, 2012.
- KRUCHTEN, P. The 4+1 View Model of Architecture. *IEEE Software*, v. 12, n. 6, p. 42-50, 1995.
- APPLE INC. Cocoa Application Layer / Model-View-Controller. Developer documentation. Available at: https://developer.apple.com/documentation/. Accessed: June 10, 2026.
- SUTHERLAND, R. Clean Swift (VIP). Available at: https://clean-swift.com. Accessed: June 10, 2026.
- POINT-FREE. Composable Architecture. GitHub repository. Available at: https://github.com/pointfreeco/swift-composable-architecture. Accessed: June 10, 2026.
- MICHEL TLUTZ. swift-patterns-studies: repository and Auth journey comparison across patterns. Available at: https://github.com/micheltlutz/swift-patterns-studies and docs/patterns/auth-comparison.md. Accessed: June 10, 2026.


