Engenharia iOS

[Arquitetura iOS #4] Diferenças entre MVP e MVVM

Há uma pergunta recorrente nas entrevistas sobre arquitetura iOS.

3 min de leitura
Imagem de capa de [Arquitetura iOS #4] Diferenças entre MVP e MVVM

Há uma pergunta recorrente nas entrevistas sobre arquitetura iOS.

“Qual é a diferença entre MVP e MVVM?”

Os dois padrões deixam os view controllers mais enxutos e colocam um objeto intermediário (Presenter/ViewModel) no centro. Olhando apenas para o diagrama, parece que só o nome de uma caixa mudou, então a resposta é surpreendentemente difícil.

Hoje, vamos resumir a diferença com um único critério: “o objeto intermediário conhece a View ou não?”


MVP: o Presenter dá comandos à View

No MVP (Model-View-Presenter), o Presenter conhece a View. Mais precisamente, ele referencia o protocolo (interface) implementado pela View.

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

final class ProfilePresenter {
    weak var view: ProfileViewProtocol?

    func load() {
        view?.showLoading(true)
        // ...Após carregar os dados
        view?.showName("João da Silva")
        view?.showLoading(false)
    }
}

O fluxo ficou claro? Depois de processar os dados, o Presenter manda diretamente para a view: “Exiba isto”. A View (view controller) implementa os métodos do protocolo e apenas desenha o que foi solicitado.

  • Vantagem: o fluxo é explícito e fácil de acompanhar, sem precisar de um framework de binding.
  • Desvantagem: quanto mais elementos a tela tiver, mais métodos o protocolo ganha. showName, showAge, showBadge

MVVM: o ViewModel não sabe que a View existe

Por outro lado, o ViewModel do MVVM (Model-View-ViewModel) não referencia a View. Ele apenas mantém o estado.

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

    func load() {
        isLoading = true
        // ...Após carregar os dados
        displayName = "João da Silva"
        isLoading = false
    }
}

Não existe uma chamada como view?.showName(...). O ViewModel atualiza apenas o próprio estado, e a View se inscreve nesse estado (binding) e o desenha automaticamente. A direção dos comandos se inverte.

  • MVP: empurra dados para a View
  • MVVM: a View observa o ViewModel para obter os dados

Por isso, o MVVM exige na prática um mecanismo de binding (Combine, @Observable etc.). O artigo anterior explicou em detalhes por que MVVM sem binding fica pela metade.

O Presenter empurra; o ViewModel recebe assinaturas
O Presenter empurra; o ViewModel recebe assinaturas

Os testes deixam a diferença evidente

O objetivo dos dois padrões é “testar a lógica sem uma tela”, mas a forma de testar é diferente.

Nos testes de MVP, é preciso criar uma View falsa e registrar as chamadas.

final class MockView: ProfileViewProtocol {
    var shownName: String?
    func showName(_ name: String) { shownName = name }
    func showLoading(_ isLoading: Bool) {}
}
// presenter.load() depois mockView.shownName validação

Nos testes de MVVM, basta verificar apenas os valores do estado, sem mock.

let vm = ProfileViewModel()
vm.load()
#expect(vm.displayName == "João da Silva")

Não são necessários protocolo nem objeto mock. Esse é o benefício prático de o ViewModel não conhecer a View.


Então, qual usar?

O critério é surpreendentemente simples: há um mecanismo de binding naturalmente disponível?

  • SwiftUI ou UIKit com Combine → MVVM é a escolha natural. O binding já vem pronto.
  • UIKit legado, em que introduzir binding é trabalhoso → MVP é mais prático. Ele funciona apenas com protocolos, mantém o fluxo explícito e facilita o onboarding.

O motivo de MVVM ter se tornado praticamente o padrão no iOS atual não é tanto ser superior ao MVP, mas sim que Combine e @Observable passaram a ser fornecidos no nível do framework, eliminando a única barreira de entrada do MVVM: o binding.

O critério é saber se o ambiente oferece binding gratuitamente
O critério é saber se o ambiente oferece binding gratuitamente

Resumo

  • A diferença essencial entre MVP e MVVM é uma só. O Presenter conhece a View (protocolo) e dá comandos diretamente; o ViewModel não conhece a View e apenas expõe o estado.
  • Por isso, MVP não precisa de binding, enquanto MVVM exige binding.
  • Nos testes, MVP precisa de uma View mock, enquanto MVVM termina apenas com a validação do estado.
  • Se o ambiente oferece binding gratuitamente (SwiftUI·Combine), use MVVM; caso contrário, MVP continua sendo uma ótima escolha.

No próximo artigo, vamos abordar o VIPER, o extremo da separação modular que leva essa ideia um passo adiante. Também vamos explicar por que apps grandes o adotaram e por que o abandonaram.

Continue lendo