MVP — apa itu, pola Model-View-Presenter di iOS dan Android

Penulis: IT Sectr Diterbitkan: 2026-02-16 Waktu membaca: 10 mnt

MVP (Model-View-Presenter) — pola arsitektur di mana Presenter bertindak sebagai perantara antara Model dan View melalui antarmuka ViewContract. Berbeda dengan MVC, di mana Controller langsung mengelola View melalui UIKit, Presenter tidak bergantung pada framework — ia bekerja melalui abstraksi, yang membuatnya dapat diuji tanpa Android SDK atau UIKit. MVP banyak digunakan dalam pengembangan Android sebelum munculnya Jetpack dan tetap relevan untuk proyek legacy. Selengkapnya di artikel Martin Fowler.

Poin Utama

  • MVP — tiga komponen: Model (data), View (antarmuka), Presenter (logika dan status)
  • ViewContract — antarmuka yang digunakan Presenter untuk berkomunikasi dengan View, memastikan testabilitas
  • Presenter — berisi semua logika bisnis, tidak bergantung pada kelas platform Android/iOS
  • Passive View — View bersifat pasif maksimal, hanya menampilkan data sesuai perintah Presenter
  • MVP vs MVC — Presenter diuji dengan unit test, Controller di MVC bergantung pada UIKit/Android Framework

Apa itu MVP: inti dari pola Model-View-Presenter

MVP (Model-View-Presenter) — pola arsitektur yang diusulkan oleh Martin Fowler di awal tahun 2000-an sebagai evolusi dari MVC untuk meningkatkan testabilitas antarmuka pengguna. Model mengelola data dan logika bisnis, View bertanggung jawab untuk menampilkan dan memproses masukan pengguna, Presenter — komponen pusat yang menerima peristiwa dari View, mengambil data dari Model, dan membentuk status untuk ditampilkan.

Perbedaan utama MVP dari MVC — Presenter tidak memiliki referensi langsung ke View. Sebaliknya, Presenter berinteraksi dengan View melalui antarmuka ViewContract. View mengimplementasikan antarmuka ini dan menyerahkan dirinya ke Presenter. Ini memutus ketergantungan pada UIKit (iOS) atau Android Framework — Presenter diuji secara terisolasi dengan implementasi mock dari ViewContract. Di MVC, controller UIViewController langsung memperbarui UILabel, di MVP Presenter memanggil metode view.showName(name), dan View memutuskan bagaimana menampilkan.

KomponenTanggung JawabTestabilitas
ModelData, logika bisnis, panggilan jaringanUnit test (tidak bergantung pada UI)
ViewMenampilkan UI, meneruskan peristiwa ke PresenterImplementasi mock melalui antarmuka
PresenterLogika bisnis, manajemen status, navigasiUnit test (melalui mock ViewContract)

Prinsip tanggung jawab tunggal di MVP dipatuhi lebih ketat daripada di MVC: View hanya bertanggung jawab untuk rendering, Model — untuk data, Presenter — untuk logika dan koordinasi. Di proyek nyata, Presenter menempati 40–60% kode layar, View — 20–30%, Model — 20–30%. Pembagian ini memungkinkan pengujian logika bisnis kunci tanpa menjalankan emulator Android atau simulator iOS.

MVP di Android: Presenter, ViewContract dan Activity

MVP di Android menggunakan Activity atau Fragment sebagai View yang mengimplementasikan ViewContract — antarmuka dengan metode untuk menampilkan data. Presenter dibuat di Activity, menghubungkan View ke dirinya, dan mengelola pemuatan data. Saat rotasi layar, Activity dibuat ulang — Presenter dapat dipertahankan melalui retain-fragment atau penyimpanan eksternal, yang memecahkan masalah kehilangan status yang khas pada MVC murni.

kotlin
// ViewContract — antarmuka untuk komunikasi Presenter dengan View
interface UserView {
    fun showLoading()
    fun hideLoading()
    fun showUser(user: User)
    fun showError(message: String)
}

// Presenter — lapisan logika yang dapat diuji
class UserPresenter(
    private val repository: UserRepository
) {
    private var view: UserView? = null

    fun attachView(view: UserView) {
        this.view = view
    }

    fun detachView() {
        view = null
    }

    fun loadUser(userId: Int) {
        view?.showLoading()
        repository.getUser(userId) { result ->
            view?.hideLoading()
            result.onSuccess { user ->
                view?.showUser(user)
            }.onFailure { e ->
                view?.showError(e.message ?: "Kesalahan tidak diketahui")
            }
        }
    }
}

// View (Activity) mengimplementasikan antarmuka
class UserActivity : AppCompatActivity(), UserView {
    private val presenter = UserPresenter(UserRepository())

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        presenter.attachView(this)
        presenter.loadUser(42)
    }

    override fun onDestroy() {
        presenter.detachView()
        super.onDestroy()
    }

    override fun showUser(user: User) { /* perbarui UI */ }
    override fun showLoading() { /* tampilkan ProgressBar */ }
    override fun hideLoading() { /* sembunyikan ProgressBar */ }
    override fun showError(message: String) { /* tampilkan Snackbar */ }
}

Manajemen siklus hidup — masalah utama MVP di Android. Activity dihancurkan saat rotasi layar, dan presenter.attachView() dipanggil kembali di onCreate(). Jika pemuatan data bersifat asinkron (RxJava, coroutine), pada saat selesai View mungkin sudah terlepas. Solusi — membatalkan langganan di detachView() atau menggunakan Loader dari Support Library (untuk proyek tanpa Jetpack). Di IT Sectr, kami bertahun-tahun menggunakan kombinasi MVP + RxJava di proyek komersial — pola ini stabil, tetapi membutuhkan disiplin dalam manajemen langganan.

Retain-fragment — mekanisme untuk mempertahankan Presenter saat rotasi layar. Fragment tanpa UI (setRetainInstance(true)) hidup lebih lama dari Activity dan menyimpan referensi ke Presenter. Saat Activity dibuat ulang, fragment meneruskan Presenter yang sama ke Activity baru. Retain-fragment sudah tidak digunakan lagi sejak AndroidX, tetapi padanan pre-Jetpack-nya (Fragment.setRetainInstance) masih berfungsi di proyek legacy. Dalam pengembangan modern, Google merekomendasikan ViewModel sebagai pengganti retain-fragment.

MVP di iOS: Presenter dan View Protocol

MVP di iOS dibangun melalui protokol View. UIViewController mengimplementasikan protokol, Presenter tidak mengimpor UIKit dan diuji secara murni. Berbeda dengan Apple MVC, di mana UIViewController sendiri berisi logika dan koneksi IBOutlet langsung, Presenter mengelola status dan memerintahkan View melalui metode protokol. View tidak membuat keputusan — ia menjalankan perintah Presenter: showUser, showLoading, navigateToProfile.

swift
import Foundation

// View Protocol — abstraksi untuk Presenter
protocol UserViewProtocol: AnyObject {
    func showLoading()
    func hideLoading()
    func display(user: User)
    func displayError(message: String)
}

// Presenter — logika murni, tanpa UIKit
final class UserPresenter {
    private weak var view: UserViewProtocol?
    private let service: UserServiceProtocol

    init(service: UserServiceProtocol) {
        self.service = service
    }

    func attach(view: UserViewProtocol) {
        self.view = view
    }

    func detach() {
        view = nil
    }

    func loadUser(id: Int) {
        view?.showLoading()
        service.fetchUser(id: id) { [weak self] result in
            guard let self else { return }
            self.view?.hideLoading()
            switch result {
            case .success(let user):
                self.view?.display(user: user)
            case .failure(let error):
                self.view?.displayError(message: error.localizedDescription)
            }
        }
    }
}

// View (UIViewController) mengimplementasikan protokol
final class UserViewController: UIViewController, UserViewProtocol {
    private let presenter = UserPresenter(service: UserService())

    override func viewDidLoad() {
        super.viewDidLoad()
        presenter.attach(view: self)
        presenter.loadUser(id: 42)
    }

    func display(user: User) {
        nameLabel.text = user.name
    }
    // ... sisa metode protokol
}

Weak reference ke View — kondisi wajib di iOS MVP. UIViewController dapat dihancurkan (pop dari tumpukan navigasi), dan closure-nya di Presenter akan membuat retain cycle. Referensi lemah (weak var) memastikan bahwa View dibebaskan saat meninggalkan layar, terlepas dari adanya operasi asinkron di Presenter. Di Android, masalah serupa diselesaikan melalui detachView() — panggilan di onDestroy() menghilangkan referensi ke View.

Passive View vs Supervising Controller — dua varian MVP dari Martin Fowler. Passive View: View tidak berisi logika, Presenter sepenuhnya mengelola status. Supervising Controller: View sendiri melakukan pengikatan data sederhana (misalnya melalui data binding), Presenter hanya campur tangan dalam skenario kompleks. Dalam pengembangan mobile, Passive View lebih sering digunakan — memberikan testabilitas maksimal dan prediktabilitas status layar.

Perbedaan MVP dengan MVC dan keuntungan pengujian

Perbedaan utama antara MVP dan MVC — cara komunikasi dengan View. Di MVC, Controller memiliki referensi langsung ke View (UIViewController.IBOutlets, Activity.findViewById). Di MVP, Presenter berinteraksi dengan View melalui antarmuka ViewContract. Perbedaan ini secara radikal mengubah testabilitas: Objek Mock yang mengimplementasikan ViewContract memungkinkan pemeriksaan logika Presenter tanpa menjalankan aplikasi, emulator, dan framework UI.

KriteriaMVCMVP
Hubungan dengan ViewLangsung (Controller → View)Melalui antarmuka (Presenter → ViewContract)
Pengujian logikaMemerlukan UIKit/Android FrameworkUnit test tanpa ketergantungan platform
Siklus hidupController hidup dengan layarPresenter bisa hidup lebih lama (retain)
KompleksitasMinimal+1 antarmuka per layar
Massive ControllerMasalah umumLogika di Presenter, View tipis

Contoh unit test Presenter di Kotlin: mock UserView dibuat, diteruskan ke Presenter, loadUser dipanggil, diperiksa apakah showUser dipanggil dengan data yang benar. Tes dijalankan dalam milidetik, tidak memerlukan emulator. Di iOS serupa — OCMock atau stub protokol memeriksa panggilan metode UserViewProtocol. Di proyek IT Sectr dengan MVP, cakupan logika bisnis dengan unit test mencapai 85–90%, 2–3 kali lebih tinggi daripada proyek MVC serupa.

Kapan MVP lebih diutamakan daripada MVC — di proyek dengan persyaratan stabilitas ketat: aplikasi perbankan, sistem medis, terminal pembayaran. Di bidang ini, biaya kesalahan tinggi dan unit test sangat penting. Di proyek Post-MVP (ketika produk sudah di pasar tetapi basis kode legacy), MVP memungkinkan pemindahan logika secara bertahap dari Massive View Controller ke lapisan yang dapat diuji tanpa menulis ulang seluruh arsitektur.

Keterbatasan MVP dan transisi ke MVVM

Kekurangan utama MVP — peningkatan jumlah antarmuka dan manajemen langganan manual. Setiap layar membutuhkan setidaknya satu ViewContract + Presenter, untuk 50 layar — 50 antarmuka dan 50 kelas Presenter. Di MVVM, ViewModel menggantikan Presenter dan menggunakan mekanisme reaktif (LiveData, StateFlow, ObservableObject), yang menghilangkan kebutuhan akan attach/detach manual dan antarmuka ViewContract.

RxJava dan MVP — kombinasi populer di Android 2015–2019. Presenter berlangganan ke Observable dari Repository, menampilkan hasil melalui ViewContract. Masalah: disposable harus dibatalkan secara eksplisit di detachView(), jika tidak kebocoran langganan akan menyebabkan crash saat memperbarui View yang terlepas. Pustaka RxLifecycle dan AutoDispose sebagian mengotomatiskan pembatalan, tetapi menambah ketergantungan. Di IT Sectr, kami beralih dari MVP+RxJava ke MVVM+Flow pada tahun 2020 — kode menjadi lebih pendek 25–30% karena hilangnya ViewContract.

Migrasi dari MVP ke MVVM — proses bertahap. 1) Ganti ViewContract dengan LiveData/StateFlow di Presenter. 2) Hapus metode attach/detach — langganan dilakukan melalui observe(). 3) Ubah nama Presenter menjadi ViewModel. 4) Integrasikan DI (Hilt/Koin) untuk ViewModelFactory. Migrasi satu layar memakan waktu 2–4 jam, seluruh basis kode — 2–4 minggu untuk proyek dengan 50–100 layar. Setelah migrasi, antarmuka ViewContract dihapus, kode menjadi lebih pendek, tes tetap ada.

MVP dalam pengembangan modern — pola ini masih hidup, tetapi kalah dari MVVM dan MVI. Google secara resmi merekomendasikan MVVM dengan Jetpack untuk proyek baru. Apple — MVVM dengan SwiftUI. Namun, pengetahuan tentang MVP wajib untuk bekerja dengan kode legacy: ratusan aplikasi Android di Google Play masih berjalan di MVP, termasuk aplikasi bank besar, peritel, dan perusahaan transportasi. Pemahaman MVP adalah fondasi untuk menguasai MVI dan Clean Architecture, karena Presenter adalah pendahulu langsung dari Use Case dalam terminologi Robert Martin.

Pertanyaan yang Sering Diajukan

Apa perbedaan MVP dengan MVC?

Di MVP, Presenter berinteraksi dengan View melalui antarmuka ViewContract, bukan secara langsung. Di MVC, Controller memiliki referensi langsung ke View melalui IBOutlet/findViewById. MVP memungkinkan pengujian Presenter dengan unit test tanpa iOS Simulator atau Android Emulator, karena Presenter tidak bergantung pada UIKit atau Android Framework. MVC memerlukan menjalankan aplikasi untuk menguji controller.

Kapan sebaiknya menggunakan MVP daripada MVVM?

MVP dibenarkan di proyek legacy yang sudah dibangun di atas pola ini dan di aplikasi tanpa dukungan mekanisme reaktif (LiveData, StateFlow, Combine). Untuk proyek baru, Google merekomendasikan MVVM dengan Jetpack (Android) dan Apple merekomendasikan MVVM dengan SwiftUI (iOS). MVP tetap menjadi pilihan terbaik untuk proyek di UIKit murni tanpa Combine ketika pengujian unit logika bisnis diperlukan.

Bagaimana cara mengatasi masalah kehilangan Presenter saat rotasi layar?

Di Android — gunakan retain-fragment (setRetainInstance(true)) atau ViewModel dari Jetpack. Retain-fragment menyimpan Presenter saat rotasi dan meneruskannya ke Activity baru. ViewModel dari Google — alternatif modern yang secara otomatis mempertahankan status saat rotasi tanpa retain-fragment. Di iOS — Presenter dibuat ulang setiap viewDidLoad, tetapi di-cache di layanan koordinator terpisah.

Berapa banyak kelas yang diperlukan untuk satu layar di MVP?

Minimal 4: antarmuka ViewContract, implementasi ViewContract (Activity/Fragment), Presenter, Model (Repository). Jika menggunakan Dagger/Hilt, modul DI ditambahkan. Untuk 50 layar, itu 200+ kelas. MVVM mengurangi jumlahnya 1 file per layar (ViewContract tidak diperlukan), MVI menambahkan kelas State dan Intent. Jumlah kelas — argumen utama menentang MVP di proyek besar.

Apa perbedaan antara Passive View dan Supervising Controller di MVP?

Passive View — View tidak berisi logika, Presenter sepenuhnya mengelola status dan data. Supervising Controller — View sendiri melakukan pengikatan sederhana (data binding), Presenter campur tangan dalam skenario kompleks. Dalam pengembangan mobile, Passive View mendominasi — memberikan testabilitas maksimal dan prediktabilitas. Supervising Controller digunakan di framework web (ASP.NET Web Forms, GWT).

Kesimpulan

  • MVP (Model-View-Presenter) — evolusi MVC dengan lapisan Presenter yang dapat diuji melalui antarmuka ViewContract
  • ViewContract — antarmuka yang mengabstraksikan View dari Presenter, memungkinkan pengujian mock
  • Passive View — varian MVP dominan di pengembangan mobile dengan View pasif
  • Presenter — berisi logika bisnis, tidak bergantung pada UIKit atau Android Framework
  • MVP vs MVC — MVP memecahkan masalah pengujian, tetapi menambah 1 antarmuka per layar
  • Retain-fragment Android — mempertahankan Presenter saat rotasi layar sebelum munculnya Jetpack ViewModel
  • Migrasi ke MVVM — penggantian ViewContract dengan LiveData/StateFlow memperpendek kode 25–30%

Kami akan mengembangkan aplikasi seluler turnkey

IT Sectr membuat aplikasi iOS dan Android untuk startup dan bisnis sejak 2017. Kami akan memberi saran dan mengusulkan solusi terbaik.

Diskusikan proyek

Baca juga