Every app has an architecture, whether you chose one or not. Architecture decides where each piece of code lives, how data flows from the network to the screen, and how painful your next feature will be.
There’s no single “best” architecture. Each one trades simplicity for structure in a different way. A weekend project and a banking app with fifty developers need very different things. This guide covers the architectures you’ll meet in real iOS codebases, so you can recognize them, understand their trade-offs, and choose wisely.
Before diving in, it helps to know a few design patterns, especially delegation, observer, and coordinator.
Each article develops a concrete Swift example, its ownership and failure contracts, testing strategy, and limitations. Read the Swift blocks in order within an article; later blocks extend earlier definitions. The framework-independent examples explain architectural contracts, while the TCA article runs against a pinned library release.
Most examples use Swift 5.7. Articles using native Observation require Swift 5.9 and the iOS 17/macOS 14 generation; their dates follow those releases. The TCA article targets 1.7.0, whose Observation support shipped in January 2024. Version and deployment requirements are called out where they matter.
Article available Coming soon
1. Model-View-Controller (MVC)
MVC in Swift: Model Rules, View Rendering, and Controller Coordination
Model-View-Controller separates domain facts, visible presentation, and the coordination between them. The useful boundary is behavioral: changing a borrowing rule should not...
MVC in Swift: Where Simplicity Helps and Coupling Starts to Hurt
MVC is neither a promise of small controllers nor a sentence to enormous ones. It is a starting arrangement of responsibilities. Its...
Refactoring Massive View Controllers in Swift Around Real Responsibilities
A massive view controller is difficult because unrelated decisions and lifetimes converge in one place. Line count is a clue, not the...
2. MVC Variants
MVC with View State in Swift: Separate Document Facts from Interaction State
MVC with explicit view state separates the document’s facts from the interaction facts needed to reconstruct a screen. A book’s title belongs...
MVC with Coordinators in Swift: Navigation Policy and Screen Lifetime
MVC with a coordinator keeps screen-level interaction in controllers while moving navigation policy and flow ownership into another object. A catalog controller...
ModelAdapter-ViewBinder in Swift: Explicit Bindings and Disposable Lifetimes
ModelAdapter-ViewBinder makes the connection between model capabilities and view properties an explicit construction artifact. Instead of putting each observation and event handler...
3. Model-View-Presenter (MVP)
MVP in Swift: A Passive View and an Explicit Presentation Contract
Model-View-Presenter gives presentation decisions an explicit owner outside the UI framework. The view reports user intent and displays a presentation contract. The...
Choosing MVP in Swift: Presentation Rules, View Contracts, and Integration Cost
MVP is useful when presentation behavior forms a meaningful contract that can be exercised independently of UIKit. It is less useful when...
4. Model-View-ViewModel (MVVM)
MVVM in Swift: State Projection, User Intent, and Request Identity
Model-View-ViewModel exposes presentation state and user intentions without requiring the view model to know concrete widgets. The view renders state and forwards...
Data Binding in Swift: Closures, Combine, and Observation Have Different Contracts
A data binding connects a source of state to a consumer. Its contract includes initial delivery, later changes, execution context, ownership, cancellation,...
MVVM with Coordinators in Swift: Typed Outputs and Owned Navigation Flows
MVVM-C separates observable presentation from navigation policy. A view model publishes screen state and emits typed outcomes. A coordinator interprets those outcomes,...
MVVM in SwiftUI: Observable State, View Identity, and Async Work
MVVM in SwiftUI works best when the view remains a projection of owned state and the view model owns a coherent feature...
5. Clean Architecture and VIPER
Clean Architecture in Swift: Dependency Direction and Honest Use-Case Boundaries
Clean Architecture organizes source dependencies so application policy does not depend on UI frameworks, databases, or transport libraries. The inner use case...
VIPER in Swift: A Complete Use Case Across Five Roles
VIPER separates a feature into View, Interactor, Presenter, Entity, and Router roles. Its value is in the boundaries: use-case work can change...
Clean Swift in Swift: Request, Response, and View Model Through the VIP Cycle
Clean Swift organizes a scene around a VIP cycle: the view sends a request to the interactor, the interactor sends a response...
When Architecture Structure Pays Off: Change Cost, Contracts, and Evidence
Extra architecture structure pays for itself when it isolates decisions that change independently or makes important failures controllable. It costs time when...
6. RIBs
RIBs in Swift: Business Lifetimes, Child Routing, and Dependency Scopes
RIBs organizes an application around business scopes and their lifetimes. A Router attaches and detaches children, an Interactor owns active business behavior,...
RIBs at Scale: Feature Ownership, Plugin Boundaries, and Session Cleanup
RIBs can be useful when independent teams need explicit feature boundaries and business lifetimes. The benefit comes from scoped dependencies, stable parent-child...
7. Unidirectional Data Flow
Redux-Style Architecture in Swift: Pure Reducers and an Ordered Store
Redux-style architecture keeps authoritative state in a store and changes it through actions interpreted by pure reducers. Views render state and dispatch...
The Elm Architecture in Swift: Model, Update, View, and Described Effects
The Elm Architecture separates Model, Update, and View. Update consumes a message and returns the next model plus descriptions of work to...
The Composable Architecture in Swift: Reducer Composition, Dependencies, and Effects
The Composable Architecture combines state, actions, reducers, dependencies, and effects into features that can be composed and tested. Its value goes beyond...
Lasso in Swift: Screen State, Actions, Outputs, and Flow Ownership
Lasso separates a screen’s state and actions from outputs consumed by a flow. The screen owns its interaction. The flow owns how...
8. Choosing an Architecture
Choosing a Swift Architecture from State, Team Boundaries, and Change Pressure
Team size influences architecture through ownership and coordination, not through a magic threshold. Twenty engineers working on independent bounded features can need...
Architecture and Testability in Swift: Deterministic Boundaries and Useful Evidence
A testable architecture lets a test control important inputs and observe meaningful outcomes. Moving code out of a view controller helps only...
UIKit and SwiftUI Architecture: Preserve Ownership Across the Framework Boundary
UIKit and SwiftUI differ in rendering and lifecycle mechanics, but neither decides who owns application state. A hybrid feature is easiest to...
Migrating Swift Architectures: One Writer, Stable Contracts, and Incremental Cutovers
Migrating architecture safely means changing ownership and dependencies while preserving product behavior. A whole-app rewrite is rarely the only route. A stable...
Up next
Testing