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) ایک آرکیٹیکچرل پیٹرن ہے جسے John Gossman نے 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 کے ذریعے UIViewController میں sink() کے ساتھ سبسکرپشن کے ساتھ نافذ کیا جاتا ہے۔ ViewModel وہی ہے، View @Published میں سبسکرپشنز کے ساتھ UIViewController ہے۔ Combine iOS 13 (2019) سے دستیاب ہے اور سسٹم میں شامل ہے — اضافی انحصار کی ضرورت نہیں ہے۔ Apple Developer Survey (2025) کے مطابق، 45% iOS پروجیکٹس UIKit کے ساتھ بھی Combine استعمال کرتے ہیں، 35% SwiftUI + Combine استعمال کرتے ہیں، 20% RxSwift (لیگیسی) استعمال کرتے ہیں۔
MVVM، 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 closure میں ڈیبگ بریک پوائنٹس کی ضرورت ہوتی ہے۔ ایک سے زیادہ StateFlows والے بڑے ViewModels میں، اگر View کسی مخصوص Flow کو سبسکرائب نہیں کرتی ہے تو UI اپ ڈیٹ چھوٹ سکتا ہے۔
جب MVP اب بھی بہتر ہے — ان پروجیکٹس میں جن کا کم از کم Android ورژن API 21 (Android 5) سے نیچے ہے، جہاں Jetpack ViewModel AndroidX کے بغیر دستیاب نہیں ہے، اور ان پروجیکٹس میں جو Combine کے بغیر خالص UIKit (iOS 12 اور نیچے) استعمال کرتے ہیں۔ لیگیسی پروجیکٹس کے لیے جہاں پورا کوڈ بیس پہلے سے MVP پر ہے، MVVM میں مکمل منتقلی ہمیشہ جائز نہیں ہے — 3 مہینوں میں 100 اسکرینز دوبارہ لکھنے سے بہتر ہے کہ سروسز میں منطق کو بتدریج نکال کر MVP کو برقرار رکھا جائے۔
ViewModel کو پلیٹ فارم انحصار کے بغیر یونٹ ٹیسٹس سے جانچا جاتا ہے — یہ MVVM کے حق میں بنیادی دلیل ہے۔ Android میں، ViewModel میں Activity، Context یا View نہیں ہوتا — تمام انحصار (Repository، UseCase) کنسٹرکٹر کے ذریعے منتقل کیے جاتے ہیں اور mock اشیاء سے تبدیل کیے جاتے ہیں۔ 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 کو wait(for: expectations, timeout: 1.0) کے ساتھ XCTestCase کے ذریعے جانچا جاتا ہے۔ UserState ساخت — منسلک اقدار کے ساتھ enum — آپریشن کے بعد اسکرین کی صحیح حالت چیک کرنے کی اجازت دیتا ہے۔
کوڈ کوریج MVVM استعمال کرنے والے IT Sectr پروجیکٹس میں 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 کے حوالے نہ رکھیں، صفائی کے وقت طویل المدت آپریشنز منسوخ کریں، closures میں weak self استعمال کریں۔
StateFlow جدید انتخاب ہے۔ LiveData آسان اور لائف سائیکل کے لیے محفوظ ہے، لیکن StateFlow زیادہ طاقتور ہے: coroutines کے ساتھ کام کرتا ہے، flatMap، combine، filter کو سپورٹ کرتا ہے، @Nullable تشریح کی ضرورت نہیں ہے۔ واحد منظر نامہ جہاں LiveData ترجیح دیتا ہے — Java کوڈ کے ساتھ کام کرنا جہاں StateFlow (Kotlin Flow API) دستیاب نہیں ہے۔ Google نئے Kotlin پروجیکٹس کے لیے StateFlow تجویز کرتا ہے۔
خلاصہ
ہم ایک موبائل ایپلیکیشن ٹرنکی تیار کریں گے
IT Sectr 2017 سے اسٹارٹ اپس اور کاروبار کے لیے iOS اور Android ایپلیکیشنز بناتا ہے۔ ہم آپ کو مشورہ دیں گے اور بہترین حل تجویز کریں گے۔
مزید پڑھیں