MVVM (Model-View-ViewModel) एक आर्किटेक्चरल पैटर्न है जिसमें ViewModel Presenter को बदलता है और View के साथ संचार के लिए रिएक्टिव तंत्र का उपयोग करता है: SwiftUI में ObservableObject, Android में LiveData/StateFlow। ViewModel के पास View का कोई संदर्भ नहीं है — डेटा सब्सक्रिप्शन के माध्यम से पास किया जाता है, जो ViewContract इंटरफ़ेस की आवश्यकता को समाप्त करता है और परीक्षण को और सरल बनाता है। Apple 2019 से SwiftUI के साथ MVVM की अनुशंसा करता है, Google Jetpack के साथ MVVM को Android की आधिकारिक आर्किटेक्चर के रूप में अनुशंसा करता है। और जानें Android Architecture Guide में।
मुख्य बातें
MVVM (Model-View-ViewModel) एक आर्किटेक्चरल पैटर्न है जिसे जॉन गॉसमैन ने 2005 में Microsoft के Windows Presentation Foundation (WPF) के लिए वर्णित किया था। ViewModel केंद्रीय घटक है जिसमें स्क्रीन की स्थिति और व्यावसायिक तर्क होता है लेकिन इसका View से कोई संदर्भ नहीं होता। डेटा रिएक्टिव बाइंडिंग तंत्र के माध्यम से पास किया जाता है: View ViewModel में परिवर्तनों की सदस्यता लेता है और डेटा बदलने पर स्वचालित रूप से पुनः रेंडर होता है।
MVVM और MVP के बीच मुख्य अंतर — ViewContract की अनुपस्थिति। MVP में, Presenter view.showUser(data) विधियों को कॉल करता है, अर्थात Presenter सक्रिय रूप से View में डेटा "धकेलता" है। MVVM में, View स्वयं सब्सक्रिप्शन के माध्यम से ViewModel से डेटा "खींचती" है: ViewModel को नहीं पता कि उसका कोई सब्सक्राइबर है या नहीं। यह अलग View की समस्या को समाप्त करता है — यदि रोटेशन पर Activity नष्ट हो जाती है, ViewModel काम करना जारी रखता है, और नई Activity बस वर्तमान डेटा की सदस्यता लेती है। IT Sectr में, हम 2020 से सभी नए प्रोजेक्ट्स में MVVM का उपयोग कर रहे हैं — कोड अधिक पूर्वानुमानित हो गया है, परीक्षण अधिक स्थिर हो गए हैं।
| घटक | जिम्मेदारी | प्लेटफ़ॉर्म |
|---|---|---|
| Model | डेटा, व्यावसायिक तर्क, रिपॉज़िटरी | Android/iOS |
| View | प्रदर्शन, ViewModel की सदस्यता | Activity/Composable, UIView/SwiftUI View |
| ViewModel | स्क्रीन स्थिति, तर्क, नेविगेशन | ViewModel (Jetpack), ObservableObject |
रिएक्टिव बाइंडिंग — MVVM की नींव। Android में, LiveData (Jetpack का हिस्सा) एक अवलोकनीय डेटा धारक है। Activity observe() के माध्यम से सदस्यता लेती है: viewModel.user.observe(this) { user -> binding.name.text = user.name }. जब user बदलता है, सभी सब्सक्राइबर स्वचालित रूप से नया मान प्राप्त करते हैं। iOS में, SwiftUI ViewModel में @Published गुणों का उपयोग करता है — परिवर्तन स्वचालित रूप से View को पुनः रेंडर करते हैं। यह MVP में आवश्यक मैन्युअल showUser/hideLoading कॉल को समाप्त करता है।
Jetpack से ViewModel — MVVM को लागू करने के लिए Google का आधिकारिक घटक। ViewModel स्क्रीन रोटेशन से बचता है: जब कॉन्फ़िगरेशन बदलता है, Activity नष्ट हो जाती है और पुनः बनाई जाती है, जबकि ViewModel मेमोरी में रहता है। नया Activity इंस्टेंस ViewModelProvider के माध्यम से वही ViewModel प्राप्त करता है। ViewModel के पास Activity, Context या View का कोई संदर्भ नहीं है — यह साफ है और Robolectric के बिना यूनिट टेस्ट से परीक्षण योग्य है।
// StateFlow के साथ ViewModel — 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) 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 -> /* लोडिंग दिखाएं */
is UserState.Success -> /* डेटा प्रदर्शित करें */
is UserState.Error -> /* त्रुटि दिखाएं */
}
}.launchIn(lifecycleScope)
viewModel.loadUser(42)
}
}
LiveData बनाम StateFlow — LiveData (2017) — Jetpack का पहला रिएक्टिव घटक, Activity जीवनचक्र के लिए अनुकूलित: onStop पर स्वचालित सदस्यता रद्दीकरण। StateFlow (2021) — Kotlin Flow कार्यान्वयन, जीवनचक्र से बंधा नहीं, लेकिन lifecycleScope के माध्यम से मैन्युअल सदस्यता रद्दीकरण की आवश्यकता होती है। StateFlow coroutines, concat, map और अन्य Flow ऑपरेटरों का समर्थन करता है, जो LiveData में नहीं हैं। IT Sectr में, हम सभी नए ViewModels के लिए StateFlow का उपयोग करते हैं — यह छोटा, अधिक शक्तिशाली और coroutines के साथ बेहतर एकीकृत होता है।
DataBinding और ViewBinding — DataBinding ViewModel को XML से @{viewModel.user.name} के माध्यम से सीधे लेआउट में जोड़ता है, Activity में कोड को समाप्त करता है। ViewBinding दृश्यों तक पहुँचने के लिए एक टाइप-सुरक्षित क्लास उत्पन्न करता है। Google सरल प्रोजेक्ट्स के लिए ViewBinding और जटिल डेटा बाइंडिंग वाले प्रोजेक्ट्स के लिए DataBinding की अनुशंसा करता है। Jetpack Compose में, DataBinding की आवश्यकता नहीं है — @Composable फ़ंक्शन State बदलने पर स्वचालित रूप से पुनः रेंडर होते हैं।
iOS में MVVM Combine से ObservableObject के माध्यम से कार्यान्वित किया जाता है। ViewModel एक क्लास है जो ObservableObject से विरासत प्राप्त करती है, जिसमें @Published गुण होते हैं। SwiftUI View @ObservedObject या @StateObject के माध्यम से ViewModel की सदस्यता लेती है। जब कोई @Published गुण बदलता है, SwiftUI स्वचालित रूप से उस View को पुनः रेंडर करता है जो इस गुण पर निर्भर करती है। Apple ने 2019 में WWDC में SwiftUI को Combine के साथ पेश किया — तब से MVVM iOS के लिए आधिकारिक रूप से अनुशंसित पैटर्न बन गया है।
import SwiftUI
import Combine
// ViewModel — @Published फ़ील्ड वाला 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 की सदस्यता लेता है
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 बनाम @ObservedObject — @StateObject ViewModel बनाता है और इसके जीवनचक्र का प्रबंधन करता है (View के जीवनकाल में एक बार)। @ObservedObject — ViewModel बाहरी रूप से बनाया जाता है और View को पास किया जाता है। WWDC 2022 निर्माण के लिए @StateObject और Views के बीच ViewModel पास करने के लिए @ObservedObject की अनुशंसा करता है। iOS 17 (2023) में, @Observable मैक्रो दिखाई दिया — जो सब्सक्रिप्शन को स्वचालित करता है और @Published एनोटेशन को समाप्त करता है। @Observable Combine का विकास है, जो iOS विकास को Kotlin Flow रिएक्टिविटी के करीब लाता है।
UIKit + MVVM — UIKit प्रोजेक्ट्स के लिए (SwiftUI के बिना), MVVM Combine और @Published के माध्यम से sink() के साथ UIViewController में सब्सक्रिप्शन के साथ कार्यान्वित किया जाता है। ViewModel वही है, View @Published में सब्सक्रिप्शन के साथ UIViewController है। Combine iOS 13 (2019) से उपलब्ध है और सिस्टम में निर्मित है — किसी अतिरिक्त निर्भरता की आवश्यकता नहीं है। Apple Developer Survey (2025) के अनुसार, 45% iOS प्रोजेक्ट UIKit के साथ भी Combine का उपयोग करते हैं, 35% SwiftUI + Combine का उपयोग करते हैं, 20% RxSwift (पुराना) का उपयोग करते हैं।
MVVP MVP से बेहतर है तीन मुख्य पहलुओं में: ViewContract इंटरफ़ेस की अनुपस्थिति, स्वचालित सब्सक्रिप्शन प्रबंधन, और स्क्रीन रोटेशन से बचना। MVP में, प्रत्येक स्क्रीन के लिए ViewContract इंटरफ़ेस + Presenter क्लास + onStart/onStop में सब्सक्रिप्शन/अनसब्सक्रिप्शन की आवश्यकता होती है। MVVM में, केवल ViewModel बनाया जाता है — Activity में सब्सक्रिप्शन मैन्युअल detach() के बिना observe() के माध्यम से किया जाता है।
| मानदंड | MVP | MVVM |
|---|---|---|
| ViewContract इंटरफ़ेस | 1 प्रति स्क्रीन | आवश्यक नहीं |
| सब्सक्रिप्शन प्रबंधन | मैन्युअल attach/detach | स्वचालित (lifecycle-aware) |
| स्क्रीन रोटेशन | Retain-fragment | ViewModel रोटेशन से बचता है |
| परीक्षण | Mock ViewContract | निर्भरता के बिना साफ क्लास |
| रिएक्टिविटी | Presenter में कॉलबैक | LiveData/StateFlow/Combine |
MVVM की कमियाँ — रिएक्टिव चेन की डिबगिंग की जटिलता और अनुचित सब्सक्रिप्शन के साथ मेमोरी लीक का जोखिम। LiveData जीवनचक्र सुरक्षा को हल करता है, StateFlow को lifecycleScope की आवश्यकता होती है, Combine को AnyCancellable के साथ sink की आवश्यकता होती है। MVP में, सभी कॉल स्पष्ट हैं (view.showUser), MVVM में डेटा एक रिएक्टिव स्ट्रीम के माध्यम से आता है — ट्रेसिंग के लिए subscribe क्लोजर में डीबग ब्रेकपॉइंट की आवश्यकता होती है। कई StateFlows वाले बड़े ViewModels में, यदि View किसी विशिष्ट Flow की सदस्यता नहीं लेती है तो आप UI अपडेट से चूक सकते हैं।
जब MVP अभी भी बेहतर है — API 21 (Android 5) से नीचे न्यूनतम Android संस्करण वाले प्रोजेक्ट्स में, जहाँ Jetpack ViewModel AndroidX के बिना उपलब्ध नहीं है, और Combine के बिना शुद्ध UIKit (iOS 12 और निचले) का उपयोग करने वाले प्रोजेक्ट्स में। पुराने प्रोजेक्ट्स के लिए जहाँ पूरा कोडबेस पहले से MVP पर है, MVVM में पूर्ण संक्रमण हमेशा उचित नहीं है — सेवाओं में तर्क के क्रमिक निष्कर्षण के साथ MVP को बनाए रखना 3 महीने में 100 स्क्रीन को फिर से लिखने से सस्ता है।
ViewModel का यूनिट टेस्ट से परीक्षण किया जाता है प्लेटफ़ॉर्म निर्भरता के बिना — यह MVVM के पक्ष में मुख्य तर्क है। Android में, ViewModel में Activity, Context या View नहीं होता — सभी निर्भरताएँ (Repository, UseCase) कंस्ट्रक्टर के माध्यम से पास की जाती हैं और मॉक ऑब्जेक्ट से बदली जाती हैं। iOS में, ObservableObject का एप्लिकेशन लॉन्च किए बिना XCTest के माध्यम से परीक्षण किया जाता है, जो स्थिरता और परीक्षण निष्पादन गति प्रदान करता है।
// MockK के साथ Android ViewModel यूनिट टेस्ट
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 का समान रूप से परीक्षण किया जाता है: mock UserService इंजेक्ट करें, loadUser कॉल करें, XCTestExpectation के माध्यम से स्थिति जाँचें। Combine Publisher का XCTestCase के माध्यम से wait(for: expectations, timeout: 1.0) के साथ परीक्षण किया जाता है। UserState संरचना — संबद्ध मानों वाला enum — ऑपरेशन के बाद स्क्रीन की सटीक स्थिति की जाँच करने की अनुमति देता है।
कोड कवरेज IT Sectr प्रोजेक्ट्स में MVVM का उपयोग करते हुए ViewModel और Repository के लिए 75–90% है। ViewModel यूनिट टेस्ट से कवर होता है, Repository परीक्षण डेटाबेस के साथ इंटीग्रेशन टेस्ट से। SwiftUI और Jetpack Compose में View का महत्वपूर्ण परिदृश्यों के लिए UI टेस्ट (XCUITest, Compose Test) से परीक्षण किया जाता है। शेष UI की जाँच स्क्रीनशॉट टेस्ट (Snapshot Testing) से की जाती है — यह UI टेस्ट से तेज़ है और प्रदर्शन सटीकता में 95% विश्वास प्रदान करता है।
अक्सर पूछे जाने वाले प्रश्न
MVVM में, ViewModel के पास View का कोई संदर्भ नहीं है — डेटा रिएक्टिव तंत्र (LiveData, StateFlow, @Published) के माध्यम से पास किया जाता है। MVP में, Presenter ViewContract इंटरफ़ेस के माध्यम से सीधे View विधियों को कॉल करता है। MVVM ViewContract और मैन्युअल attach/detach को समाप्त करता है, लेकिन रिएक्टिव स्ट्रीम की समझ की आवश्यकता होती है। ViewModel Android पर स्क्रीन रोटेशन से बचता है, Presenter को retain-fragment की आवश्यकता होती है।
न्यूनतम सेट: lifecycle-viewmodel-ktx (ViewModel), lifecycle-livedata-ktx या kotlinx-coroutines-core (StateFlow)। इंजेक्शन के लिए — Hilt या Koin। अतुल्यकालिक संचालन के लिए — Kotlin Coroutines। जटिल डेटा बाइंडिंग के लिए — DataBinding। Jetpack Compose (2022 से Google द्वारा अनुशंसित) में, compose-runtime और lifecycle-viewmodel-compose पर्याप्त हैं।
SwiftUI (2019) रिएक्टिव आर्किटेक्चर के लिए डिज़ाइन किया गया है: @State और @Published डेटा बदलने पर स्वचालित रूप से View को पुनः रेंडर करते हैं। MVVM SwiftUI के लिए एक प्राकृतिक फिट है: View — @ViewBuilder, ViewModel — ObservableObject। Apple MVVM को एकमात्र पैटर्न के रूप में लागू नहीं करता, लेकिन 2019 से सभी प्रशिक्षण सामग्री ViewModel + SwiftUI का उपयोग करती है। UIKit के लिए, Apple MVC या Coordinator की अनुशंसा करता है।
Android: viewModelScope ViewModel साफ होने पर स्वचालित रूप से coroutines रद्द करता है। iOS: Combine से AnyCancellable धारक ऑब्जेक्ट के डीलोकेट होने पर स्वचालित रूप से सब्सक्रिप्शन रद्द करता है। SwiftUI @StateObject स्वचालित रूप से जीवनचक्र का प्रबंधन करता है। मुख्य नियम: ViewModel में View/Context के संदर्भ न रखें, सफाई पर लंबे समय तक चलने वाले संचालन रद्द करें, क्लोजर में weak self का उपयोग करें।
StateFlow आधुनिक विकल्प है। LiveData सरल और जीवनचक्र-सुरक्षित है, लेकिन StateFlow अधिक शक्तिशाली है: coroutines के साथ काम करता है, flatMap, combine, filter का समर्थन करता है, @Nullable एनोटेशन की आवश्यकता नहीं है। एकमात्र परिदृश्य जहाँ LiveData बेहतर है — Java कोड के साथ काम करना जहाँ StateFlow (Kotlin Flow API) उपलब्ध नहीं है। Google नए Kotlin प्रोजेक्ट्स के लिए StateFlow की अनुशंसा करता है।
सारांश
हम एक मोबाइल एप्लिकेशन टर्नकी विकसित करेंगे
IT Sectr 2017 से स्टार्टअप और व्यवसायों के लिए iOS और Android एप्लिकेशन बनाता है। हम आपको सलाह देंगे और सर्वोत्तम समाधान प्रस्तावित करेंगे।
यह भी पढ़ें