AndroidX ViewModel به عنوان یک پل عمل می کند و یک قرارداد واضح بین منطق تجاری مشترک و اجزای رابط کاربری شما ایجاد می کند. این الگو به اطمینان از سازگاری دادهها در بین پلتفرمها کمک میکند، در حالی که رابطهای کاربری را قادر میسازد برای ظاهر متمایز هر پلتفرم سفارشی شوند. میتوانید با Jetpack Compose در Android و SwiftUI در iOS به توسعه UI خود ادامه دهید.
درباره مزایای استفاده از ViewModel و همه ویژگیهای مستندات اولیه ViewModel بیشتر بخوانید.
وابستگی ها را تنظیم کنید
برای راه اندازی KMP ViewModel در پروژه خود، وابستگی را در فایل libs.versions.toml
تعریف کنید:
[versions]
androidx-viewmodel = 2.9.3
[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)
}
API های ViewModel را برای دسترسی از سویفت صادر کنید
بهطور پیشفرض، هر کتابخانهای که به پایگاه کد خود اضافه میکنید، بهطور خودکار به چارچوب باینری صادر نمیشود. اگر API ها صادر نمی شوند، فقط در صورتی از چارچوب باینری در دسترس هستند که از آنها در کد مشترک استفاده کنید (از مجموعه منبع iosMain
یا commonMain
). در آن صورت، APIها حاوی پیشوند بسته خواهند بود، برای مثال یک کلاس ViewModel
به عنوان کلاس Lifecycle_viewmodelViewModel
در دسترس خواهد بود. برای اطلاعات بیشتر در مورد صادرات وابستگی ها ، وابستگی های صادراتی به باینری ها را بررسی کنید.
برای بهبود تجربه، میتوانید وابستگی ViewModel را به چارچوب باینری با استفاده از تنظیمات export
در فایل build.gradle.kts
صادر کنید، جایی که چارچوب باینری iOS را تعریف میکنید، که باعث میشود APIهای ViewModel مستقیماً از کد سوئیفت مانند کد 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
هنگام اجرای برنامه های مشترک در 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
sourceSet وجود ندارد. تنها نکته این است که شما نمی توانید از هیچ API مخصوص پلتفرم استفاده کنید و باید آنها را انتزاع کنید. به عنوان مثال، اگر از یک Application
Android به عنوان پارامتر سازنده ViewModel استفاده می کنید، باید با انتزاع کردن آن از API خارج شوید.
اطلاعات بیشتر درباره نحوه استفاده از کد پلتفرم خاص در کد پلتفرم خاص در Kotlin Multiplatform موجود است.
به عنوان مثال، در قطعه زیر یک کلاس 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 از همه ویژگی های کاتلین یا سوئیفت پشتیبانی نمی کنند ، نمی توانید یک ViewModel از نوع عمومی را مستقیماً از سویفت بازیابی کنید.
برای حل این مشکل، میتوانید یک تابع کمکی ایجاد کنید که از 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] }
سپس، وقتی میخواهید تابع را از سویفت فراخوانی کنید، میتوانید یک تابع عمومی از نوع T : ViewModel
و از T.self
استفاده کنید، که میتواند ObjCClass
به تابع resolveViewModel
منتقل کند.
دامنه ViewModel را به چرخه حیات SwiftUI متصل کنید
مرحله بعدی ایجاد یک 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. چرخه عمر آن ViewModel ها زمانی که صفحه ای که از IosViewModelStoreOwner
استفاده می کند پاک می شود و deinit
فراخوانی می کند. میتوانید در اسناد رسمی درباره بیاصلسازی اطلاعات بیشتری کسب کنید.
در این مرحله، میتوانید IosViewModelStoreOwner
بهعنوان یک @StateObject
در یک نمای SwiftUI نمونهسازی کنید و تابع 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 موجود نیست
برخی از APIهای موجود در اندروید در کاتلین چندپلتفرم در دسترس نیستند.
ادغام با Hilt
از آنجایی که Hilt برای پروژههای چند پلتفرمی Kotlin در دسترس نیست، نمیتوانید مستقیماً از ViewModels با حاشیهنویسی @HiltViewModel
در sourceSet commonMain
استفاده کنید. در این صورت باید از چارچوب DI جایگزین استفاده کنید، به عنوان مثال، Koin ، kotlin-inject ، Metro ، یا Kodein . میتوانید تمام چارچوبهای DI را که با Kotlin Multiplatform کار میکنند در klibs.io پیدا کنید.
جریان ها را در SwiftUI مشاهده کنید
مشاهده Coroutines Flow در SwiftUI مستقیماً پشتیبانی نمی شود. با این حال، میتوانید از KMP-NativeCoroutines یا کتابخانه SKIE برای اجازه دادن به این ویژگی استفاده کنید.