App Architecture with MVVM
A clean architecture keeps apps maintainable as they grow. In this final lesson you'll structure a SwiftUI app with MVVM — separating the Model , View , and ViewModel — using ObservableObject view models for separation of concerns and easy testing.
Learn App Architecture with MVVM in our free Swift course — a beginner-friendly interactive lesson with worked examples, a practice exercise and a quick…
Part of the free Swift course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.
What You'll Learn in This Lesson
- • What MVVM is: Model, View, and ViewModel roles
- • Keep the Model pure: data and domain rules, no UI
- • Build an ObservableObject ViewModel with @Published state
- • Keep the View thin, bound to ViewModel state
- • Achieve clear separation of concerns
- • Unit-test the ViewModel without any UI
📊 The Three Roles
Layer
Owns
Knows about
Model
Data & domain rules
Nothing about UI
ViewModel
Presentation logic & state
The Model
View
Layout & user actions
The ViewModel
1️⃣ The Model
The Model is pure data and domain rules. It imports no UI framework, so it can be reused and tested anywhere.
2️⃣ The ViewModel
The ViewModel is an ObservableObject that prepares model data for the view and holds presentation logic. Its @Published state drives the UI.
Your turn. Fill in the protocol and the publishing wrapper.
3️⃣ The View (and Testing the ViewModel)
The View stays thin: it shows ViewModel state and forwards actions. Because the ViewModel is UI-independent, you can unit-test its logic directly with XCTest.
Now you try. Fill in the wrapper that owns the view model and the method call.
Pro Tips
- 💡 Keep the Model UI-free. If it imports SwiftUI, it's drifting out of its lane.
- 💡 Own the view model with @StateObject when the view creates it; use @ObservedObject when it's passed in.
- 💡 Inject services via protocols so the ViewModel can be tested with mocks.
- 💡 Push logic down. If a View grows an if tangle, move it into the ViewModel.
Common Errors (and the fix)
- View model resets unexpectedly — you used @ObservedObject for a VM the view creates; use @StateObject .
- View doesn't update — the changing property isn't marked @Published .
- Logic stuck in the View — move calculations and decisions into the ViewModel.
- Model imports SwiftUI — keep the Model UI-free so it stays reusable and testable.
📋 Quick Reference: MVVM Recap
Responsibility
Key tools
Tested how
struct, enum
plain unit tests
ObservableObject, @Published
XCTest, no UI
@StateObject, body
UI / preview
Binding
View observes VM
@StateObject / @ObservedObject
auto-refresh
Frequently Asked Questions
Mini-Challenge: Weather in MVVM
Structure a small weather screen as Model, ViewModel, and View, then test the ViewModel — the perfect capstone for the track.
🎉 Track Complete!
- ✅ MVVM = Model (data), View (UI), ViewModel (presentation logic)
- ✅ The Model stays UI-free; the View stays thin
- ✅ The ViewModel is an ObservableObject with @Published state
- ✅ Views own view models with @StateObject
- ✅ Separation of concerns makes the ViewModel easy to unit-test
- ✅ Congratulations — you've finished the Building iOS Apps with SwiftUI track!
Practice quiz
What do the three letters in MVVM stand for?
- Model-View-ViewModel
- Model-Value-View-Module
- Main-View-Model-Manager
- Module-View-ViewMaker
Answer: Model-View-ViewModel. MVVM is Model, View, and ViewModel.
What is the Model's responsibility?
- Drawing the UI
- Representing the app's data and business rules
- Handling taps
- Storing view state only
Answer: Representing the app's data and business rules. The Model holds data and domain logic, independent of the UI.
What does the View do in MVVM?
- Stores the database
- Displays state and forwards user actions
- Performs networking
- Owns business rules
Answer: Displays state and forwards user actions. The View shows what the ViewModel exposes and relays user input.
What is the ViewModel's job?
- Render pixels
- Prepare data for the view and hold presentation logic
- Define the storage schema
- Replace the Model entirely
Answer: Prepare data for the view and hold presentation logic. The ViewModel transforms model data into view-ready state and handles presentation logic.
Which protocol do SwiftUI view models commonly adopt?
- Codable
- Hashable
- ObservableObject
- Sendable
Answer: ObservableObject. ObservableObject lets a view model publish changes that the view observes.
Inside an ObservableObject, which wrapper publishes a property's changes?
- @State
- @Published
- @Binding
- @Environment
Answer: @Published. @Published broadcasts changes so observing views refresh.
Which wrapper does a SwiftUI view use to observe a view model it creates and owns?
- @State
- @StateObject
- @Published
- @FetchRequest
Answer: @StateObject. @StateObject creates and owns an ObservableObject for the view's lifetime.
A key benefit of MVVM is...
- Fewer files always
- No need for models
- Faster compile times guaranteed
- Separation of concerns and easier testing of logic
Answer: Separation of concerns and easier testing of logic. MVVM separates UI from logic, making the logic easy to test in isolation.
Why is a ViewModel easy to unit test?
- It holds plain logic with no UI dependency
- It imports SwiftUI heavily
- It runs on the GPU
- It cannot be tested
Answer: It holds plain logic with no UI dependency. Because a ViewModel is UI-independent, you can test its logic directly without rendering views.
In MVVM, the View should ideally contain...
- All business logic
- The data model
- Little logic — mostly layout bound to ViewModel state
- The networking code
Answer: Little logic — mostly layout bound to ViewModel state. Views stay thin, delegating logic to the ViewModel for testability and clarity.
Continue this course
- Previous: Swift Package Manager
- Next: Custom Property Wrappers