AndroidX ViewModel, paylaşılan iş mantığınız ile kullanıcı arayüzü bileşenleriniz arasında net bir sözleşme oluşturarak köprü görevi görür. Bu desen, platformlar arasında veri tutarlılığını sağlamaya yardımcı olurken kullanıcı arayüzlerinin her platformun kendine özgü görünümüne göre özelleştirilmesini sağlar. Android'de Jetpack Compose, iOS'te ise SwiftUI ile kullanıcı arayüzünüzü geliştirmeye devam edebilirsiniz.
ViewModel kullanmanın avantajları ve tüm özellikler hakkında daha fazla bilgiyi ViewModel'in birincil dokümanında bulabilirsiniz.
Bağımlılıkları ayarlama
Projenizde KMP ViewModel'i ayarlamak için bağımlılığı libs.versions.toml
dosyasında tanımlayın:
[versions]
androidx-viewmodel = 2.9.3
[libraries]
androidx-lifecycle-viewmodel = { module = "androidx.lifecycle:lifecycle-viewmodel", version.ref = "androidx-viewmodel" }
Ardından, KMP modülünüz için build.gradle.kts
dosyasına yapıyı ekleyin ve bağımlılığı api
olarak bildirin. Bu bağımlılık, ikili çerçeveye aktarılacaktır:
// 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)
}
Swift'ten erişim için ViewModel API'lerini dışa aktarma
Varsayılan olarak, kod tabanınıza eklediğiniz hiçbir kitaplık ikili çerçeveye otomatik olarak aktarılmaz. API'ler dışa aktarılmazsa yalnızca paylaşılan kodda (iosMain
veya commonMain
kaynak kümesinden) kullanırsanız ikili çerçeveden kullanılabilir. Bu durumda API'ler paket önekini içerir. Örneğin, ViewModel
sınıfı Lifecycle_viewmodelViewModel
sınıfı olarak kullanılabilir. Bağımlılıkları dışa aktarma hakkında daha fazla bilgi için Bağımlılıkları ikili dosyalara aktarma başlıklı makaleyi inceleyin.
Deneyimi iyileştirmek için ViewModel bağımlılığını, iOS ikili çerçevesini tanımladığınız build.gradle.kts
dosyasındaki export
kurulumunu kullanarak ikili çerçeveye aktarabilirsiniz. Bu işlem, ViewModel API'lerine Kotlin kodunda olduğu gibi doğrudan Swift kodundan erişilmesini sağlar:
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"
}
}
(İsteğe bağlı) JVM masaüstünde viewModelScope
kullanma
ViewModel'de eş yordamlar çalıştırılırken viewModelScope
özelliği Dispatchers.Main.immediate
'ye bağlıdır. Bu özellik, masaüstünde varsayılan olarak kullanılamayabilir. Doğru şekilde çalışması için projenize kotlinx-coroutines-swing
bağımlılığını ekleyin:
// Optional if you use JVM Desktop
desktopMain.dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-swing:[KotlinX Coroutines version]")
}
Daha fazla ayrıntı için Dispatchers.Main
belgelerini inceleyin.
commonMain
veya androidMain
ViewModel'ini kullanma
ViewModel sınıfının paylaşılan commonMain
veya androidMain
sourceSet'te kullanılmasıyla ilgili özel bir şart yoktur. Tek dikkat etmeniz gereken nokta, platforma özgü API'leri kullanamayacağınız ve bunları soyutlamanız gerektiğidir. Örneğin, ViewModel oluşturucu parametresi olarak bir Android Application
kullanıyorsanız bu API'yi soyutlayarak bu API'den geçiş yapmanız gerekir.
Platforma özgü kodun nasıl kullanılacağı hakkında daha fazla bilgiyi Kotlin Multiplatform'da platforma özgü kod başlıklı makalede bulabilirsiniz.
Örneğin, aşağıdaki snippet'te commonMain
içinde tanımlanmış, fabrikasıyla birlikte bir ViewModel sınıfı yer alıyor:
// 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()
Ardından, kullanıcı arayüzü kodunuzda ViewModel'i her zamanki gibi alabilirsiniz:
// androidApp/ui/MainScreen.kt @Composable fun MainScreen( viewModel: MainViewModel = viewModel( factory = mainViewModelFactory, ), ) { // observe the viewModel state }
SwiftUI'da ViewModel'i kullanma
Android'de ViewModel yaşam döngüsü otomatik olarak işlenir ve ComponentActivity
, Fragment
, NavBackStackEntry
(Navigation 2) veya rememberViewModelStoreNavEntryDecorator
(Navigation 3) ile kapsamlandırılır. Ancak iOS'teki SwiftUI'da AndroidX ViewModel'in yerleşik bir eşdeğeri yoktur.
ViewModel'i SwiftUI uygulamanızla paylaşmak için bazı kurulum kodları eklemeniz gerekir.
Jeneriklerle ilgili yardımcı işlev oluşturma
Genel bir ViewModel örneğini oluşturmak için Android'de sınıf referansı yansıtma özelliği kullanılır. Objective-C jenerikleri, Kotlin veya Swift'in tüm özelliklerini desteklemediğinden jenerik türdeki bir ViewModel'i doğrudan Swift'ten alamazsınız.
Bu soruna yardımcı olması için, genel tür yerine ObjCClass
kullanacak bir yardımcı işlev oluşturabilir ve ardından ViewModel sınıfını almak için getOriginalKotlinClass
kullanabilirsiniz:
// 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] }
Ardından, işlevi Swift'ten çağırmak istediğinizde T : ViewModel
türünde genel bir işlev yazabilir ve ObjCClass
değerini resolveViewModel
işlevine aktarabilen T.self
değerini kullanabilirsiniz.
ViewModel kapsamını SwiftUI yaşam döngüsüne bağlama
Bir sonraki adım, ObservableObject
ve ViewModelStoreOwner
arayüzlerini (protokoller) uygulayan bir IosViewModelStoreOwner
oluşturmaktır. ObservableObject
, bu sınıfı SwiftUI kodunda @StateObject
olarak kullanabilmek için eklenir:
// 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() } }
Bu sahip, Android'de olduğu gibi birden fazla ViewModel türünün alınmasına izin verir.
Bu ViewModel'lerin yaşam döngüsü, IosViewModelStoreOwner
kullanan ekranın başlatması kaldırıldığında ve deinit
çağrıldığında temizlenir. Başlatmayı kaldırma hakkında daha fazla bilgiyi resmi belgelerde bulabilirsiniz.
Bu noktada, IosViewModelStoreOwner
öğesini SwiftUI View'da @StateObject
olarak başlatabilir ve ViewModel almak için viewModel
işlevini çağırabilirsiniz:
// 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'da kullanılamaz
Android'de kullanılabilen bazı API'ler Kotlin Multiplatform'da kullanılamaz.
Hilt ile entegrasyon
Hilt, Kotlin Multiplatform projelerinde kullanılamadığından @HiltViewModel
ek açıklamalı ViewModel'leri commonMain
sourceSet'te doğrudan kullanamazsınız. Bu durumda, Koin, kotlin-inject, Metro veya Kodein gibi alternatif bir DI çerçevesi kullanmanız gerekir. Kotlin Multiplatform ile çalışan tüm DI çerçevelerini klibs.io adresinde bulabilirsiniz.
SwiftUI'da akışları gözlemleme
SwiftUI'da coroutine akışlarını gözlemlemek doğrudan desteklenmez. Ancak bu özelliğe izin vermek için KMP-NativeCoroutines veya SKIE kitaplığını kullanabilirsiniz.