MVVM: apa itu, pola Model-View-ViewModel dalam pengembangan mobile

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

MVVM (Model-View-ViewModel) — pola arsitektur di mana ViewModel menggantikan Presenter dan menggunakan mekanisme reaktif untuk berkomunikasi dengan View: ObservableObject di SwiftUI, LiveData/StateFlow di Android. ViewModel tidak memiliki referensi ke View — data dikirim melalui langganan, yang menghilangkan kebutuhan akan antarmuka ViewContract dan membuat pengujian lebih sederhana. Apple merekomendasikan MVVM dengan SwiftUI sejak 2019, Google — MVVM dengan Jetpack sebagai arsitektur resmi Android. Selengkapnya di Android Architecture Guide.

Utama

  • MVVM — Model (data), View (antarmuka), ViewModel (status dan logika tanpa referensi ke View)
  • Reactive binding — LiveData, StateFlow, ObservableObject secara otomatis memperbarui UI saat data berubah
  • ViewModel — bertahan dari rotasi layar dan tidak bergantung pada Android SDK/UIKit, diuji dengan unit-test
  • Android Jetpack — ViewModel, LiveData, DataBinding — stack resmi dari Google untuk MVVM
  • SwiftUI + Combine — implementasi native MVVM di iOS dengan @Published dan @ObservedObject

Apa itu MVVM: esensi pola Model-View-ViewModel

MVVM (Model-View-ViewModel) — pola arsitektur yang dijelaskan oleh John Gossman pada tahun 2005 untuk Windows Presentation Foundation (WPF) dari Microsoft. ViewModel — komponen sentral yang berisi status layar dan logika bisnis, tetapi tidak memiliki referensi ke View. Data dikirim melalui mekanisme pengikatan reaktif: View berlangganan perubahan ViewModel dan secara otomatis digambar ulang saat data berubah.

Perbedaan utama MVVM dari MVP — tidak adanya ViewContract. Di MVP, Presenter memanggil metode view.showUser(data), artinya Presenter secara aktif “mendorong” data ke View. Di MVVM, View sendiri “menarik” data dari ViewModel melalui langganan: ViewModel tidak tahu apakah ia memiliki pelanggan. Ini menghilangkan masalah View yang terlepas — jika Activity dihancurkan saat rotasi, ViewModel terus bekerja, dan Activity baru cukup berlangganan data terkini. Di IT Sectr kami menggunakan MVVM di semua proyek baru sejak 2020 — kode menjadi lebih dapat diprediksi, pengujian lebih stabil.

KomponenTanggung JawabPlatform
ModelData, logika bisnis, repositoriAndroid/iOS
ViewTampilan, langganan ke ViewModelActivity/Composable, UIView/SwiftUI View
ViewModelStatus layar, logika, navigasiViewModel (Jetpack), ObservableObject

Pengikatan reaktif — fondasi MVVM. Di Android, LiveData (bagian dari Jetpack) adalah penyimpanan data yang dapat diamati. Activity berlangganan melalui observe(): viewModel.user.observe(this) { user -> binding.name.text = user.name }. Saat user berubah, semua pelanggan secara otomatis menerima nilai baru. Di iOS, SwiftUI menggunakan properti @Published di ViewModel — perubahan secara otomatis menggambar ulang View. Ini menghilangkan panggilan manual showUser/hideLoading yang diperlukan di MVP.

MVVM di Android: ViewModel, LiveData dan StateFlow

ViewModel dari Jetpack — komponen resmi dari Google untuk mengimplementasikan MVVM. ViewModel bertahan dari rotasi layar: saat konfigurasi berubah, Activity dihancurkan dan dibuat ulang, sementara ViewModel tetap di memori. Instance Activity baru menerima ViewModel yang sama melalui ViewModelProvider. ViewModel tidak memiliki referensi ke Activity, Context atau View — murni dan diuji dengan unit-test tanpa Robolectric.

kotlin
// ViewModel dengan StateFlow — implementasi modern MVVM
class UserViewModel(
    private val repository: UserRepository
) : ViewModel() {

    private val _state = MutableStateFlow<UserState>(UserState.Loading)
    val state: StateFlow<UserState> = _state.asStateFlow()

    fun loadUser(userId: Int) {
        viewModelScope.launch {
            _state.value = UserState.Loading
            repository.getUser(userId)
                .onSuccess { user ->
                    _state.value = UserState.Success(user)
                }
                .onFailure { e ->
                    _state.value = UserState.Error(e.message ?: "Unknown")
                }
        }
    }
}

sealed interface UserState {
    data object Loading : UserState
    data class Success(val user: User) : UserState
    data class Error(val message: String) : UserState
}

// View (Activity) berlangganan state
class UserActivity : AppCompatActivity() {
    private val viewModel: UserViewModel by viewModels()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        viewModel.state.onEach { state ->
            when (state) {
                is UserState.Loading -> /* tampilkan loading */
                is UserState.Success -> /* tampilkan data */
                is UserState.Error -> /* tampilkan kesalahan */
            }
        }.launchIn(lifecycleScope)
        viewModel.loadUser(42)
    }
}

LiveData vs StateFlow — LiveData (2017) — komponen reaktif pertama Jetpack, dioptimalkan untuk siklus hidup Activity: berhenti berlangganan otomatis saat onStop. StateFlow (2021) — implementasi Kotlin Flow, tidak terikat pada siklus hidup, tetapi memerlukan penghentian langganan manual melalui lifecycleScope. StateFlow mendukung coroutines, concat, map dan operator Flow lainnya yang tidak ada di LiveData. Di IT Sectr kami menggunakan StateFlow untuk semua ViewModel baru — lebih pendek, lebih kuat, dan terintegrasi lebih baik dengan coroutine.

DataBinding dan ViewBinding — DataBinding menghubungkan ViewModel dengan XML melalui @{viewModel.user.name} langsung di tata letak, menghilangkan kode di Activity. ViewBinding menghasilkan kelas type-safe untuk akses ke View. Google merekomendasikan ViewBinding untuk proyek sederhana dan DataBinding untuk proyek dengan pengikatan data yang kompleks. Di Jetpack Compose, DataBinding tidak diperlukan — fungsi @Composable secara otomatis digambar ulang saat State berubah.

MVVM di iOS: ObservableObject dan SwiftUI

MVVM di iOS diimplementasikan melalui ObservableObject dari Combine. ViewModel — kelas yang mewarisi ObservableObject, dengan bidang @Published. SwiftUI View berlangganan ke ViewModel melalui @ObservedObject atau @StateObject. Saat properti @Published berubah, SwiftUI secara otomatis menggambar ulang View yang bergantung pada properti ini. Apple memperkenalkan SwiftUI pada tahun 2019 di WWDC bersama dengan Combine — sejak saat itu MVVM menjadi pola yang direkomendasikan secara resmi untuk iOS.

swift
import SwiftUI
import Combine

// ViewModel — ObservableObject dengan bidang @Published
final class UserViewModel: ObservableObject {
    @Published private(set) var state: UserState = .loading
    private let service: UserService

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

    func loadUser(id: Int) {
        state = .loading
        service.fetchUser(id: id) { [weak self] result in
            guard let self else { return }
            switch result {
            case .success(let user):
                self.state = .success(user)
            case .failure(let error):
                self.state = .error(error.localizedDescription)
            }
        }
    }
}

enum UserState {
    case loading
    case success(User)
    case error(String)
}

// SwiftUI View — berlangganan ke ViewModel
struct UserView: View {
    @StateObject private var viewModel: UserViewModel

    var body: some View {
        switch viewModel.state {
        case .loading:
            ProgressView()
        case .success(let user):
            VStack {
                Text(user.name).font(.title)
                Text(user.email).font(.body)
            }
        case .error(let message):
            Text(message).foregroundColor(.red)
        }
    }
}

@StateObject vs @ObservedObject — @StateObject membuat ViewModel dan mengelola siklus hidupnya (sekali selama masa hidup View). @ObservedObject — ViewModel dibuat dari luar dan diteruskan ke View. WWDC 2022 merekomendasikan @StateObject untuk pembuatan dan @ObservedObject untuk meneruskan ViewModel antar Views. Di iOS 17 (2023) muncul @Observable — makro yang mengotomatiskan langganan dan menghilangkan anotasi @Published. @Observable — evolusi Combine, yang mendekatkan pengembangan iOS ke reaktivitas Kotlin Flow.

UIKit + MVVM — untuk proyek di UIKit (tanpa SwiftUI), MVVM diimplementasikan melalui Combine dan @Published dengan langganan di UIViewController melalui sink(). ViewModel sama, View — UIViewController dengan langganan ke @Published. Combine tersedia sejak iOS 13 (2019) dan terintegrasi dalam sistem — tidak memerlukan dependensi tambahan. Menurut Apple Developer Survey (2025), 45% proyek iOS menggunakan Combine bahkan dengan UIKit, 35% menggunakan SwiftUI + Combine, 20% — RxSwift (legacy).

Perbandingan MVVM dengan MVP: kelebihan dan kekurangan

MVVM unggul dari MVP dalam tiga aspek utama: tidak adanya antarmuka ViewContract, manajemen langganan otomatis, dan bertahan dari rotasi layar. Di MVP, setiap layar memerlukan antarmuka ViewContract + kelas Presenter + langganan/berhenti langganan di onStart/onStop. Di MVVM hanya ViewModel yang dibuat — langganan di Activity dilakukan melalui observe() tanpa detach manual.

KriteriaMVPMVVM
Antarmuka ViewContract1 per layarTidak diperlukan
Manajemen langgananManual attach/detachOtomatis (lifecycle-aware)
Rotasi layarRetain-fragmentViewModel bertahan
PengujianMock ViewContractKelas murni tanpa dependensi
ReaktivitasCallback di PresenterLiveData/StateFlow/Combine

Kekurangan MVVM — kompleksitas debugging rantai reaktif dan risiko kebocoran memori saat langganan yang tidak tepat. LiveData memecahkan masalah keamanan siklus hidup, StateFlow memerlukan lifecycleScope, Combine — sink dengan AnyCancellable. Di MVP semua panggilan eksplisit (view.showUser), di MVVM data datang melalui aliran reaktif — pelacakan memerlukan breakpoint debug di closure subscribe. Di ViewModel besar dengan banyak StateFlow, pembaruan UI dapat terlewatkan jika View tidak berlangganan ke Flow tertentu.

Kapan MVP masih lebih baik — dalam proyek dengan versi minimum Android di bawah API 21 (Android 5), di mana Jetpack ViewModel tidak tersedia tanpa AndroidX, dan dalam proyek di UIKit murni tanpa Combine (iOS 12 ke bawah). Untuk proyek legacy di mana seluruh basis kode sudah di MVP, transisi penuh ke MVVM tidak selalu dibenarkan — lebih murah mempertahankan MVP dengan pemindahan logika bertahap ke layanan, daripada menulis ulang 100 layar dalam 3 bulan.

Pengujian ViewModel di Android dan iOS

ViewModel diuji dengan unit-test tanpa dependensi platform — ini adalah argumen utama untuk MVVM. Di Android, ViewModel tidak berisi Activity, Context atau View — semua dependensi (Repository, UseCase) diteruskan melalui konstruktor dan diganti dengan objek mock. Di iOS, ObservableObject diuji melalui XCTest tanpa menjalankan aplikasi, yang memberikan stabilitas dan kecepatan eksekusi pengujian.

kotlin
// Unit-test Android ViewModel dengan MockK
class UserViewModelTest {

    private val repository = mockk<UserRepository>()
    private val viewModel = UserViewModel(repository)

    @Test
    fun loadUser_success_updatesState() = runTest {
        val user = User(1, "John", "john@test.com")
        coEvery { repository.getUser(1) } returns Result.success(user)

        viewModel.loadUser(1)

        assertEquals(UserState.Success(user), viewModel.state.value)
    }

    @Test
    fun loadUser_error_updatesErrorState() = runTest {
        val error = RuntimeException("Network error")
        coEvery { repository.getUser(1) } returns Result.failure(error)

        viewModel.loadUser(1)

        val state = viewModel.state.value
        assertTrue(state is UserState.Error)
        assertEquals("Network error", (state as UserState.Error).message)
    }
}

iOS ViewModel diuji dengan cara serupa: kita menyuntikkan mock UserService, memanggil loadUser, memeriksa status melalui XCTestExpectation. Combine Publisher diuji melalui XCTestCase dengan wait(for: expectations, timeout: 1.0). Struktur UserState — enum dengan associated values — memungkinkan pemeriksaan status layar yang tepat setelah operasi.

Cakupan kode di proyek IT Sectr pada MVVM adalah 75-90% untuk ViewModel dan Repository. ViewModel dicakup oleh unit-test, Repository — oleh pengujian integrasi dengan database pengujian. View di SwiftUI dan Jetpack Compose diuji dengan pengujian UI (XCUITest, Compose Test) untuk skenario kritis. Sisa UI diperiksa dengan pengujian tangkapan layar (Snapshot Testing) — ini lebih cepat daripada pengujian UI dan memberikan 95% keyakinan akan kebenaran tampilan.

Pertanyaan yang Sering Diajukan

Apa perbedaan utama antara MVVM dan MVP?

Di MVVM, ViewModel tidak memiliki referensi ke View — data dikirim melalui mekanisme reaktif (LiveData, StateFlow, @Published). Di MVP, Presenter langsung memanggil metode View melalui antarmuka ViewContract. MVVM menghilangkan ViewContract dan attach/detach manual, tetapi memerlukan pemahaman tentang aliran reaktif. ViewModel bertahan dari rotasi layar di Android, Presenter memerlukan retain-fragment.

Pustaka apa yang diperlukan untuk MVVM di Android?

Set minimum: lifecycle-viewmodel-ktx (ViewModel), lifecycle-livedata-ktx atau kotlinx-coroutines-core (StateFlow). Untuk injeksi — Hilt atau Koin. Untuk operasi asinkron — Kotlin Coroutines. Untuk pengikatan data yang kompleks — DataBinding. Di Jetpack Compose (direkomendasikan oleh Google sejak 2022) compose-runtime dan lifecycle-viewmodel-compose sudah cukup.

Mengapa Apple merekomendasikan MVVM untuk iOS?

SwiftUI (2019) dirancang untuk arsitektur reaktif: @State dan @Published secara otomatis menggambar ulang View saat data berubah. MVVM — kecocokan alami untuk SwiftUI: View — @ViewBuilder, ViewModel — ObservableObject. Apple tidak memaksakan MVVM sebagai satu-satunya pola, tetapi semua materi pelatihan sejak 2019 menggunakan ViewModel + SwiftUI. Untuk UIKit, Apple merekomendasikan MVC atau Coordinator.

Bagaimana cara menghindari kebocoran memori di ViewModel?

Android: viewModelScope secara otomatis membatalkan coroutine saat pembersihan ViewModel. iOS: AnyCancellable dari Combine secara otomatis berhenti berlangganan saat objek yang menyimpannya dibebaskan. SwiftUI @StateObject mengelola siklus hidup secara otomatis. Aturan utama: jangan menyimpan referensi ke View/Context di ViewModel, batalkan operasi berjalan lama saat pembersihan, gunakan weak self di closure.

Apa yang dipilih: LiveData atau StateFlow untuk Android?

StateFlow — pilihan modern. LiveData lebih sederhana dan aman untuk siklus hidup, tetapi StateFlow lebih kuat: bekerja dengan coroutine, mendukung flatMap, combine, filter, tidak memerlukan anotasi @Nullable. Satu-satunya skenario di mana LiveData lebih disukai — bekerja dengan kode Java, di mana StateFlow (Kotlin Flow-API) tidak tersedia. Google merekomendasikan StateFlow untuk proyek baru di Kotlin.

Kesimpulan

  • MVVM (Model-View-ViewModel) — pola reaktif di mana ViewModel tidak memiliki referensi ke View
  • ViewModel — bertahan dari rotasi layar, diuji dengan unit-test, independen dari UI
  • Android — ViewModel + StateFlow + Kotlin Coroutines — stack modern dari Google
  • iOS — ObservableObject + @Published + SwiftUI — implementasi native MVVM
  • MVVM vs MVP — MVVM menghilangkan ViewContract dan attach/detach manual
  • Pengujian — ViewModel dicakup oleh unit-test tanpa dependensi platform
  • Rekomendasi — MVVM untuk proyek baru; MVP untuk dukungan legacy

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