MVVM (Model-View-ViewModel) — ViewModel Presenter o'rnini egallaydigan va View bilan aloqa qilish uchun reaktiv mexanizmlardan foydalanadigan arxitektura patternidir: SwiftUI-da ObservableObject, Android-da LiveData/StateFlow. ViewModel View-ga havolaga ega emas — ma'lumotlar obuna orqali uzatiladi, bu ViewContract interfeyslariga bo'lgan ehtiyojni yo'q qiladi va testlashni yanada osonlashtiradi. Apple 2019-yildan SwiftUI bilan MVVM-ni, Google esa Jetpack bilan MVVM-ni Android-ning rasmiy arxitekturasi sifatida tavsiya qiladi. Batafsil Android Architecture Guide-da.
Asosiy
MVVM (Model-View-ViewModel) — 2005-yilda John Gossman tomonidan Microsoft-ning Windows Presentation Foundation (WPF) uchun tasvirlangan arxitektura patternidir. ViewModel — ekran holati va biznes mantiqini o'z ichiga olgan, ammo View-ga havolasi bo'lmagan markaziy komponentdir. Ma'lumotlar reaktiv bog'lash mexanizmlari orqali uzatiladi: View ViewModel o'zgarishlariga obuna bo'ladi va ma'lumotlar o'zgarganda avtomatik ravishda qayta chiziladi.
MVVM va MVP o'rtasidagi asosiy farq — ViewContract yo'qligi. MVP-da Presenter view.showUser(data) usullarini chaqiradi, ya'ni Presenter faol ravishda ma'lumotlarni View-ga “itaradi”. MVVM-da View o'zi ma'lumotlarni ViewModel-dan obuna orqali “toradi”: ViewModel obunachisi bor-yo'qligini bilmaydi. Bu ajratilgan View muammosini bartaraf qiladi — agar Activity aylanish vaqtida yo'q qilinsa, ViewModel ishni davom ettiradi, yangi Activity esa shunchaki dolzarb ma'lumotlarga obuna bo'ladi. IT Sectr-da 2020-yildan beri barcha yangi loyihalarda MVVM-dan foydalanamiz — kod oldindan taxmin qilinadigan, testlar barqaror bo'ldi.
| Komponent | Mas'uliyat | Platforma |
|---|---|---|
| Model | Ma'lumotlar, biznes mantiq, repozitoriylar | Android/iOS |
| View | Ko'rsatish, ViewModel-ga obuna | Activity/Composable, UIView/SwiftUI View |
| ViewModel | Ekran holati, mantiq, navigatsiya | ViewModel (Jetpack), ObservableObject |
Reaktiv bog'lash — MVVM-ning asosi. Android-da LiveData (Jetpack qismi) kuzatiladigan ma'lumot omboridir. Activity observe() orqali obuna bo'ladi: viewModel.user.observe(this) { user -> binding.name.text = user.name }. user o'zgarganda barcha obunachilar avtomatik ravishda yangi qiymatni oladi. iOS-da SwiftUI ViewModel-da @Published xususiyatlaridan foydalanadi — o'zgarishlar avtomatik ravishda View-ni qayta chizadi. Bu MVP-da talab qilinadigan qo'lda showUser/hideLoading chaqiruvlarini yo'q qiladi.
Jetpack-dan ViewModel — Google-dan MVVM-ni tatbiq qilish uchun rasmiy komponent. ViewModel ekran aylanishidan omon qoladi: konfiguratsiya o'zgarganda Activity yo'q qilinadi va qayta yaratiladi, ViewModel esa xotirada qoladi. Activity-ning yangi namunasi ViewModelProvider orqali bir xil ViewModel-ni oladi. ViewModel Activity, Context yoki View-ga havolaga ega emas — u toza va Robolectric holda unit-testlar bilan tekshiriladi.
// ViewModel StateFlow bilan — MVVM-ning zamonaviy tatbiqi
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) state-ga obuna bo'ladi
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 -> /* yuklashni ko'rsatish */
is UserState.Success -> /* ma'lumotlarni ko'rsatish */
is UserState.Error -> /* xatoni ko'rsatish */
}
}.launchIn(lifecycleScope)
viewModel.loadUser(42)
}
}
LiveData vs StateFlow — LiveData (2017) — Activity hayot aylanishi uchun optimallashtirilgan birinchi Jetpack reaktiv komponenti: onStop-da avtomatik obunani bekor qilish. StateFlow (2021) — hayot aylanishiga bog'liq bo'lmagan, ammo lifecycleScope orqali qo'lda obunani bekor qilishni talab qiladigan Kotlin Flow tatbiqi. StateFlow coroutines, concat, map va LiveData-da mavjud bo'lmagan boshqa Flow operatorlarini qo'llab-quvvatlaydi. IT Sectr-da barcha yangi ViewModel-lar uchun StateFlow-dan foydalanamiz — u qisqaroq, kuchliroq va korutinlar bilan yaxshiroq integratsiyalashadi.
DataBinding va ViewBinding — DataBinding ViewModel-ni XML bilan @{viewModel.user.name} orqali to'g'ridan-to'g'ri layoutda bog'laydi, Activity-dagi kodni yo'q qiladi. ViewBinding View-ga kirish uchun tip-xavfsiz sinf yaratadi. Google oddiy loyihalar uchun ViewBinding, murakkab ma'lumot bog'lashi bo'lgan loyihalar uchun DataBinding tavsiya qiladi. Jetpack Compose-da DataBinding kerak emas — @Composable funksiyalari State o'zgarganda avtomatik qayta chiziladi.
iOS-da MVVM Combine-dan ObservableObject orqali tatbiq qilinadi. ViewModel — ObservableObject-dan meros oladigan sinf, @Published maydonlari bilan. SwiftUI View @ObservedObject yoki @StateObject orqali ViewModel-ga obuna bo'ladi. @Published xususiyati o'zgarganda SwiftUI ushbu xususiyatga bog'liq bo'lgan View-ni avtomatik qayta chizadi. Apple SwiftUI-ni 2019-yilda WWDC-da Combine bilan birga taqdim etdi — shu paytdan boshlab MVVM iOS uchun rasman tavsiya etilgan pattern bo'ldi.
import SwiftUI
import Combine
// ViewModel — @Published maydonlari bilan ObservableObject
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 — ViewModel-ga obuna bo'ladi
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 ViewModel yaratadi va uning hayot aylanishini boshqaradi (View hayoti davomida bir marta). @ObservedObject — ViewModel tashqarida yaratiladi va View-ga uzatiladi. WWDC 2022 yaratish uchun @StateObject va ViewModel-ni Views o'rtasida uzatish uchun @ObservedObject tavsiya qiladi. iOS 17 (2023) da @Observable paydo bo'ldi — obunani avtomatlashtiradigan va @Published annotatsiyalarini yo'q qiladigan makro. @Observable — Combine evolyutsiyasi bo'lib, iOS ishlanmasini Kotlin Flow reaktivligiga yaqinlashtiradi.
UIKit + MVVM — UIKit-da (SwiftUI holda) loyihalar uchun MVVM Combine va @Published orqali UIViewController-da sink() bilan obuna bo'lib tatbiq qilinadi. ViewModel bir xil, View — @Published-ga obuna bo'lgan UIViewController. Combine iOS 13 (2019) dan boshlab mavjud va tizimga o'rnatilgan — qo'shimcha bog'liqliklarni talab qilmaydi. Apple Developer Survey (2025) ma'lumotiga ko'ra, iOS loyihalarining 45% hatto UIKit-da Combine-dan foydalanadi, 35% SwiftUI + Combine-dan foydalanadi, 20% — RxSwift (legacy).
MVVM MVP-dan ustun uchta asosiy jihatda: ViewContract interfeyslarining yo'qligi, obunalarni avtomatik boshqarish va ekran aylanishidan omon qolish. MVP-da har bir ekran ViewContract interfeysi + Presenter sinfi + onStart/onStop-da obuna/obunani bekor qilishni talab qiladi. MVVM-da faqat ViewModel yaratiladi — Activity-da obuna observe() orqali qo'lda detach() holda amalga oshiriladi.
| Mezon | MVP | MVVM |
|---|---|---|
| ViewContract interfeyslari | 1 ekranga | Kerak emas |
| Obuna boshqaruvi | Qo'lda attach/detach | Avtomatik (lifecycle-aware) |
| Ekran aylanishi | Retain-fragment | ViewModel omon qoladi |
| Testlash | Mock ViewContract | Bog'liqliklarsiz toza sinf |
| Reaktivlik | Presenter-da callback-lar | LiveData/StateFlow/Combine |
MVVM kamchiliklari — reaktiv zanjirlarni disk raskadrovka qilish murakkabligi va noto'g'ri obuna paytida xotira oqishi xavfi. LiveData hayot aylanishi xavfsizligi muammosini hal qiladi, StateFlow lifecycleScope talab qiladi, Combine — AnyCancellable bilan sink. MVP-da barcha chaqiruvlar ochiq (view.showUser), MVVM-da ma'lumotlar reaktiv oqim orqali keladi — kuzatish subscribe closure-ida debug breakpoint-larini talab qiladi. Ko'p StateFlow-li katta ViewModel-larda View ma'lum bir Flow-ga obuna bo'lmasa, UI yangilanishini o'tkazib yuborish mumkin.
MVP qachon yaxshiroq — minimal Android versiyasi API 21 dan (Android 5) past bo'lgan loyihalarda, Jetpack ViewModel AndroidX holda mavjud emas, va Combine holda toza UIKit-da (iOS 12 va past) loyihalarda. Hamma kod bazasi allaqachon MVP-da bo'lgan legacy loyihalar uchun MVVM-ga to'liq o'tish har doim ham oqlanmaydi — 3 oy ichida 100 ekranni qayta yozishdan ko'ra, bosqichma-bosqich mantiqni xizmatlarga chiqarib MVP-ni saqlash arzonroq.
ViewModel platforma bog'liqliklarisiz unit-testlar bilan tekshiriladi — bu MVVM foydasiga asosiy argumentdir. Android-da ViewModel Activity, Context yoki View-ni o'z ichiga olmaydi — barcha bog'liqliklar (Repository, UseCase) konstruktor orqali uzatiladi va mock obyektlari bilan almashtiriladi. iOS-da ObservableObject ilovani ishga tushirmasdan XCTest orqali tekshiriladi, bu testlarning barqarorligi va tezligini ta'minlaydi.
// MockK bilan Android ViewModel unit-testi
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 xuddi shunday tekshiriladi: mock UserService joylashtiramiz, loadUser chaqiramiz, holatni XCTestExpectation orqali tekshiramiz. Combine Publisher XCTestCase orqali wait(for: expectations, timeout: 1.0) bilan tekshiriladi. UserState tuzilmasi — associated values bilan enum — operatsiyadan keyin ekranning aniq holatini tekshirish imkonini beradi.
Kod qamrovi IT Sectr-ning MVVM-dagi loyihalarida ViewModel va Repository uchun 75-90% ni tashkil qiladi. ViewModel unit-testlar bilan, Repository test ma'lumotlar bazasi bilan integratsiya testlari bilan qoplanadi. SwiftUI va Jetpack Compose-da View kritik stsenariylar uchun UI testlari (XCUITest, Compose Test) bilan tekshiriladi. Qolgan UI skrinshot testlari (Snapshot Testing) bilan tekshiriladi — bu UI testlaridan tezroq va ko'rsatish to'g'riligiga 95% ishonch beradi.
Ko'p beriladigan savollar
MVVM-da ViewModel View-ga havolaga ega emas — ma'lumotlar reaktiv mexanizmlar (LiveData, StateFlow, @Published) orqali uzatiladi. MVP-da Presenter to'g'ridan-to'g'ri ViewContract interfeysi orqali View usullarini chaqiradi. MVVM ViewContract va qo'lda attach/detach-ni yo'q qiladi, ammo reaktiv oqimlarni tushunishni talab qiladi. ViewModel Android-da ekran aylanishidan omon qoladi, Presenter retain-fragment talab qiladi.
Minimal to'plam: lifecycle-viewmodel-ktx (ViewModel), lifecycle-livedata-ktx yoki kotlinx-coroutines-core (StateFlow). Inyeksiya uchun — Hilt yoki Koin. Asinxron operatsiyalar uchun — Kotlin Coroutines. Murakkab ma'lumot bog'lash uchun — DataBinding. Jetpack Compose-da (Google tomonidan 2022-yildan tavsiya etilgan) compose-runtime va lifecycle-viewmodel-compose yetarli.
SwiftUI (2019) reaktiv arxitektura uchun yaratilgan: @State va @Published ma'lumot o'zgarganda View-ni avtomatik qayta chizadi. MVVM SwiftUI-ga tabiiy moslikdir: View — @ViewBuilder, ViewModel — ObservableObject. Apple MVVM-ni yagona pattern sifatida majburlamaydi, ammo 2019-yildan boshlab barcha o'quv materiallari ViewModel + SwiftUI dan foydalanadi. UIKit uchun Apple MVC yoki Coordinator ni tavsiya qiladi.
Android: viewModelScope ViewModel tozalanayotganda korutinlarni avtomatik bekor qiladi. iOS: Combine-dan AnyCancellable uni saqlaydigan obyekt bo'shatilganda avtomatik obunani bekor qiladi. SwiftUI @StateObject hayot aylanishini avtomatik boshqaradi. Asosiy qoidalar: ViewModel-da View/Context havolalarini saqlamaslik, tozalashda uzoq muddatli operatsiyalarni bekor qilish, closure-larda weak self ishlatish.
StateFlow — zamonaviy tanlov. LiveData soddaroq va hayot aylanishi uchun xavfsiz, ammo StateFlow kuchliroq: korutinlar bilan ishlaydi, flatMap, combine, filter-ni qo'llab-quvvatlaydi, @Nullable annotatsiyasini talab qilmaydi. LiveData afzal bo'lgan yagona stsenariy — StateFlow (Kotlin Flow-API) mavjud bo'lmagan Java kodi bilan ishlash. Google Kotlin-dagi yangi loyihalar uchun StateFlow-ni tavsiya qiladi.
Xulosa
Biz kalit topshirig'i bilan mobil ilovani ishlab chiqamiz
IT Sectr 2017-yildan beri startaplar va korxonalar uchun iOS va Android ilovalarini yaratadi. Biz sizga maslahat beramiz va eng yaxshi yechimni taklif qilamiz.