iOS Engineering

[iOS Architecture #4] MVP vs MVVM: Key Differences

One question comes up repeatedly in iOS architecture interviews.

3 min read
Cover image for [iOS Architecture #4] MVP vs MVVM: Key Differences

One question comes up repeatedly in iOS architecture interviews.

“What is the difference between MVP and MVVM?”

Both patterns slim down view controllers and place an intermediary object (Presenter/ViewModel) in the middle. Looking only at the diagram, it seems like just one box has been renamed, which makes this surprisingly tricky to answer.

Today, let’s reduce the difference to one criterion: “Does the intermediary object know about the View or not?”


MVP: The Presenter commands the View

In MVP (Model-View-Presenter), the Presenter knows about the View. More precisely, it references a protocol (interface) implemented by the View.

protocol ProfileViewProtocol: AnyObject {
    func showName(_ name: String)
    func showLoading(_ isLoading: Bool)
}

final class ProfilePresenter {
    weak var view: ProfileViewProtocol?

    func load() {
        view?.showLoading(true)
        // ...After loading data
        view?.showName("John Doe")
        view?.showLoading(false)
    }
}

Can you see the flow? After processing the data, the Presenter directly commands the view, “Display this”. The View (view controller) implements the protocol methods and simply renders what it is told.

  • Advantage: The flow is explicit and easy to follow, with no binding framework required.
  • Disadvantage: As the number of screen elements grows, protocol methods keep multiplying. showName, showAge, showBadge

MVVM: The ViewModel Does Not Know the View Exists

By contrast, the ViewModel in MVVM (Model-View-ViewModel) does not reference the View. It only holds state.

final class ProfileViewModel {
    @Published private(set) var displayName = ""
    @Published private(set) var isLoading = false

    func load() {
        isLoading = true
        // ...After loading data
        displayName = "John Doe"
        isLoading = false
    }
}

There is no call like view?.showName(...). The ViewModel updates only its own state, and the View subscribes to (binds to) that state and renders it automatically. The direction of the command is reversed.

  • MVP: Push data to the View
  • MVVM: The View observes the ViewModel to pull data

That is why MVVM effectively requires a binding mechanism (Combine, @Observable, and so on). The previous installment covered in detail why MVVM without binding is only half an implementation.

The Presenter pushes; the ViewModel is observed
The Presenter pushes; the ViewModel is observed

The Difference Becomes Clear in the Tests

Both patterns aim to test logic without a screen, but the testing approaches differ.

MVP tests create a fake View and record its calls.

final class MockView: ProfileViewProtocol {
    var shownName: String?
    func showName(_ name: String) { shownName = name }
    func showLoading(_ isLoading: Bool) {}
}
// presenter.load() after mockView.shownName verification

MVVM tests are done by simply checking the state values, without mocks.

let vm = ProfileViewModel()
vm.load()
#expect(vm.displayName == "John Doe")

No protocol or mock object is needed. That is the practical benefit of the ViewModel not knowing about the View.


So Which One Should You Use?

The criterion is surprisingly simple: is a binding mechanism available naturally?

  • SwiftUI, or UIKit with Combine → MVVM is the natural choice. Binding comes for free.
  • Legacy UIKit where introducing binding feels burdensome → MVP is more practical. It runs on protocols alone, keeps the flow explicit, and is easy to onboard.

The reason MVVM has become something close to the de facto standard in modern iOS is less that it is superior to MVP and more that Combine and @Observable are provided at the framework level, removing MVVM’s only barrier to entry: binding.

The choice depends on whether the environment provides binding for free
The choice depends on whether the environment provides binding for free

Summary

  • The essential difference between MVP and MVVM is just one thing. The Presenter knows the View (protocol) and commands it directly, while the ViewModel does not know the View and only exposes state.
  • Therefore, MVP needs no binding, while MVVM requires it.
  • In testing, MVP requires a mock View, while MVVM is complete with state verification alone.
  • In an environment where binding is free (SwiftUI·Combine), choose MVVM; otherwise, MVP is still a perfectly good choice.

In the next installment, we’ll cover VIPER, the extreme form of modular separation that takes this one step further. We’ll also examine why large apps adopted it—and why they abandoned it.