AndroidX ViewModel بهعنوان یک پل عمل میکند و قراردادی واضح بین منطق کسبوکار مشترک شما و مؤلفههای واسط کاربر شما برقرار میکند. این الگو به اطمینان از سازگاری دادهها در پلاتفرمهای مختلف کمک میکند و درعینحال امکان سفارشیسازی واسطهای کاربر را برای ظاهر متمایز هر پلاتفرم فراهم میکند. میتوانید به توسعه میانای کاربریتان با Jetpack Compose در Android و SwiftUI در iOS ادامه دهید.
درباره مزایای استفاده از ViewModel و همه ویژگیها در سند اصلی ViewModel بیشتر بخوانید.
راهاندازی وابستگیها
برای راهاندازی KMP ViewModel در پروژهتان، وابستگی را در فایل
libs.versions.toml تعریف کنید:
[versions]
androidx-viewmodel = 2.11.0
[libraries]
androidx-lifecycle-viewmodel = { module = "androidx.lifecycle:lifecycle-viewmodel", version.ref = "androidx-viewmodel" }
سپس، خروجی ساختاریافته را به فایل build.gradle.kts برای واحد KMP خود اضافه کنید
و وابستگی را بهعنوان api اعلام کنید، زیرا این وابستگی به
چارچوب دودویی صادر خواهد شد:
// You need the "api" dependency declaration here if you want better access to the classes from Swift code.
commonMain.dependencies {
api(libs.androidx.lifecycle.viewmodel)
}
صادر کردن ViewModel APIs برای دسترسی از Swift
بهطور پیشفرض، هر کتابخانهای که به پایگاه کدتان اضافه کنید بهطور خودکار به چارچوب دودویی صادر نخواهد شد. اگر میاناهای برنامهسازی کاربردی صادر نشوند،
فقط درصورتی از چارچوب دودویی دردسترس هستند که از آنها در کد مشترک
(از مجموعه منبع iosMain یا commonMain) استفاده کنید. در این حالت، میاناهای برنامهسازی کاربردی
حاوی پیشوند بسته خواهند بود، برای مثال کلاس ViewModel بهصورت
کلاس Lifecycle_viewmodelViewModel دردسترس خواهد بود. برای اطلاعات بیشتر درباره صادر کردن وابستگیها، صادر کردن وابستگیها به دودوییها را بررسی کنید.
برای بهبود تجربه، میتوانید وابستگی ViewModel را بااستفاده از راهاندازی export در فایل build.gradle.kts که در آن چارچوب دودویی iOS را تعریف میکنید به چارچوب دودویی صادر کنید، که باعث میشود میاناهای برنامهسازی کاربردی ViewModel مستقیماً از کد Swift قابلدسترسی باشند، همانطور که از کد Kotlin قابلدسترسی هستند:
listOf(
iosX64(),
iosArm64(),
iosSimulatorArm64(),
).forEach {
it.binaries.framework {
// Add this line to all the targets you want to export this dependency
export(libs.androidx.lifecycle.viewmodel)
baseName = "shared"
}
}
(اختیاری) استفاده از viewModelScope در JVM Desktop
هنگام اجرای روالهای همزمان در ViewModel، ویژگی viewModelScope به Dispatchers.Main.immediate گره خورده است که ممکن است بهطور پیشفرض در رایانه دردسترس نباشد. برای اینکه درست کار کند، وابستگی kotlinx-coroutines-swing را به پروژهتان اضافه کنید:
// Optional if you use JVM Desktop
desktopMain.dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-swing:[KotlinX Coroutines version]")
}
برای جزئیات بیشتر، به اسناد Dispatchers.Main مراجعه کنید.
استفاده از ViewModel از commonMain یا androidMain
هیچ الزام خاصی برای استفاده از کلاس ViewModel در commonMain مشترک یا از منبع androidMain وجود ندارد. تنها نکتهای که باید درنظر بگیرید این است که
نمیتوانید از میاناهای برنامهسازی کاربردی مختص پلاتفرم استفاده کنید و باید آنها را انتزاعی کنید. برای مثال، اگر از Android Application بهعنوان پارامتر سازنده ViewModel استفاده میکنید، باید با انتزاعی کردن این API، از آن مهاجرت کنید.
اطلاعات بیشتر درباره نحوه استفاده از کد مختص پلاتفرم در کد مختص پلاتفرم در Kotlin چندپلاتفرمی دردسترس است.
برای مثال، در گزیده زیر یک کلاس ViewModel با کارخانه آن وجود دارد،
که در commonMain تعریف شده است:
// commonMain/MainViewModel.kt class MainViewModel( private val repository: DataRepository, ) : ViewModel() { /* some logic */ } // ViewModelFactory that retrieves the data repository for your app. val mainViewModelFactory = viewModelFactory { initializer { MainViewModel(repository = getDataRepository()) } } fun getDataRepository(): DataRepository = DataRepository()
سپس، در کد UI خود، میتوانید ViewModel را طبق معمول بازیابی کنید:
// androidApp/ui/MainScreen.kt @Composable fun MainScreen( viewModel: MainViewModel = viewModel( factory = mainViewModelFactory, ), ) { // observe the viewModel state }
استفاده از ViewModel از SwiftUI
در Android، چرخه حیات ViewModel بهطور خودکار مدیریت میشود و به
ComponentActivity، Fragment، NavBackStackEntry (Navigation 2)، یا
rememberViewModelStoreNavEntryDecorator (Navigation 3) محدود میشود. بااینحال، SwiftUI در iOS
معادل داخلی برای AndroidX ViewModel ندارد.
برای همرسانی ViewModel با برنامه SwiftUI، باید مقداری کد راهاندازی اضافه کنید.
ایجاد تابعی برای کمک به انواع عمومی
نمونهسازی یک نمونه ViewModel عمومی از ویژگی بازتاب مرجع کلاس در Android استفاده میکند. چون Objective-C عمومی از همه ویژگیهای Kotlin یا Swift پشتیبانی نمیکند، نمیتوانید مستقیماً ViewModel نوع عمومی را از Swift بازیابی کنید.
برای کمک به حل این مشکل، میتوانید تابع کمکی ایجاد کنید که بهجای نوع عمومی از
ObjCClass استفاده کند و سپس از getOriginalKotlinClass
برای بازیابی کلاس ViewModel برای نمونهسازی استفاده کنید:
// iosMain/ViewModelResolver.ios.kt /** * This function allows retrieving any ViewModel from Swift Code with generics. We only get * [ObjCClass] type for the [modelClass], because the interop between Kotlin and Swift code * doesn't preserve the generic class, but we can retrieve the original KClass in Kotlin. */ @BetaInteropApi @Throws(IllegalArgumentException::class) fun ViewModelStore.resolveViewModel( modelClass: ObjCClass, factory: ViewModelProvider.Factory, key: String?, extras: CreationExtras? = null, ): ViewModel { @Suppress("UNCHECKED_CAST") val vmClass = getOriginalKotlinClass(modelClass) as? KClass<ViewModel> require(vmClass != null) { "The modelClass parameter must be a ViewModel type." } val provider = ViewModelProvider.Companion.create(this, factory, extras ?: CreationExtras.Empty) return key?.let { provider[key, vmClass] } ?: provider[vmClass] }
سپس، وقتی میخواهید تابع را از Swift فراخوانی کنید، میتوانید یک تابع عمومی از نوع T : ViewModel بنویسید و از T.self استفاده کنید که میتواند ObjCClass را به تابع resolveViewModel منتقل کند.
متصل کردن دامنه ViewModel به SwiftUI Lifecycle
مرحله بعدی ایجاد IosViewModelStoreOwner است که میاناهای (پروتکلهای)
ObservableObject و ViewModelStoreOwner را پیادهسازی میکند. دلیل
ObservableObject این است که بتوان از این کلاس بهعنوان @StateObject
در کد SwiftUI استفاده کرد:
// iosApp/IosViewModelStoreOwner.swift class IosViewModelStoreOwner: ObservableObject, ViewModelStoreOwner { let viewModelStore = ViewModelStore() /// This function allows retrieving the androidx ViewModel from the store. /// It uses the utilify function to pass the generic type T to shared code func viewModel<T: ViewModel>( key: String? = nil, factory: ViewModelProviderFactory, extras: CreationExtras? = nil ) -> T { do { return try viewModelStore.resolveViewModel( modelClass: T.self, factory: factory, key: key, extras: extras ) as! T } catch { fatalError("Failed to create ViewModel of type \(T.self)") } } /// This is called when this class is used as a `@StateObject` deinit { viewModelStore.clear() } }
این مالک امکان بازیابی چندین نوع ViewModel را فراهم میکند، مشابه آنچه در Android وجود دارد.
چرخه حیات آن ViewModels زمانی پاک میشود که صفحهای که از
IosViewModelStoreOwner استفاده میکند مقداردهی اولیه نشده باشد و deinit را فراخوانی کند. میتوانید در
اسناد رسمی
درباره لغو مقداردهی اولیه بیشتر بدانید.
در این مرحله، میتوانید IosViewModelStoreOwner را بهعنوان
@StateObject در SwiftUI View نمونهسازی کنید و تابع viewModel را برای بازیابی
ViewModel فراخوانی کنید:
// iosApp/ContentView.swift struct ContentView: View { /// Use the store owner as a StateObject to allow retrieving ViewModels and scoping it to this screen. @StateObject private var viewModelStoreOwner = IosViewModelStoreOwner() var body: some View { /// Retrieves the `MainViewModel` instance using the `viewModelStoreOwner`. /// The `MainViewModel.Factory` and `creationExtras` are provided to enable dependency injection /// and proper initialization of the ViewModel with its required `AppContainer`. let mainViewModel: MainViewModel = viewModelStoreOwner.viewModel( factory: MainViewModelKt.mainViewModelFactory ) // ... // .. the rest of the SwiftUI code } }
در Kotlin Multiplatform دردسترس نیست
برخیاز میاناهای برنامهسازی کاربردی که در Android دردسترس هستند، در Kotlin Multiplatform دردسترس نیستند.
ادغام با Hilt
چون Hilt برای پروژههای Kotlin Multiplatform دردسترس نیست،
نمیتوانید مستقیماً از ViewModels با حاشیهنویسی @HiltViewModel در
commonMain sourceSet استفاده کنید. در این صورت باید از چارچوب DI جایگزین دیگری استفاده کنید، برای مثال، Koin، kotlin-inject، Metro، یا Kodein. میتوانید همه چارچوبهای DI را که با Kotlin Multiplatform کار میکنند در klibs.io پیدا کنید.
مشاهده جریانها در SwiftUI
مشاهده جریانهای روالهای همزمان در SwiftUI مستقیماً پشتیبانی نمیشود. بااینحال، میتوانید از کتابخانه KMP-NativeCoroutines یا SKIE برای مجاز کردن این ویژگی استفاده کنید.