MVVM done right

Architecture & patterns Mid-level Swift 5.9+ iOS 17+ Reviewed By Alok Singh

Spot the bug

This view model breaks MVVM. Tap the lines that do.

  1. @Observable @MainActor
  2. final class ProfileViewModel {
  3. var name = ""
  4. var errorMessage: String?
  5. var nameLabel: UILabel?
  6. private let service: ProfileService
  7. init(service: ProfileService) { self.service = service }
  8. func load() async {
  9. do {
  10. name = try await service.fetchName()
  11. nameLabel?.text = name
  12. } catch {
  13. errorMessage = error.localizedDescription
  14. }
  15. }
  16. }
Hint 1 of 1

Does a view model know about views?

Choose an answer, then open Show answer below to check it.

Show answer
  • Answer: line 5 var nameLabel: UILabel?
  • Answer: line 11 nameLabel?.text = name

Lines 5 and 11 make the view model hold and drive a UIKit view. Expose name only and let the view render it.

30-second answer

The model holds data and business rules. The view model turns model data into display-ready state, handles user intents and calls injected services, without importing UIKit or holding views. The view renders that state and forwards actions. In SwiftUI, make the view model @Observable and @MainActor, and inject its dependencies so it can be tested without UI.

Practise saying it · 45 s. No recording, just a timer

Explain MVVM and its rules.

Say your answer out loud, then check which of these points you covered:

  • Model, view model, view
  • State not views
  • Injected services
  • Testable

Deep dive

Expose state, not views: strings, flags and arrays of display items.

One view model per screen or feature, not per row unless rows have behaviour.

Keep navigation out of the view model, or behind a router protocol.

Test view models with stubbed services.

Common mistakes

  • View models holding UILabel or UIViewController references.
  • View models that are just pass-throughs.

Senior take & follow-ups

Seniors keep the view model free of UI types and fully testable.

Follow-up questions

Think of your answer first, then open the hint.

  1. Should a view model import SwiftUI?

    Show hint

    Ideally not; it should be testable without UI frameworks.

  2. How does a view model trigger navigation?

    Show hint

    A callback or router protocol.

Suggest a fix if something here is wrong or out of date.