AndroidX ViewModel, paylaşılan iş mantığınız ile kullanıcı arayüzü bileşenleriniz arasında net bir sözleşme oluşturan bir köprü görevi görür. Bu kalıp, platformlar arasında veri tutarlılığı sağlarken kullanıcı arayüzlerinin her platformun farklı görünümüne göre özelleştirilmesine olanak tanır. 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 ile ilgili birincil dokümanlarda bulabilirsiniz.
Bağımlılıkları ayarlama
Projenizde KMP ViewModel'i ayarlamak için libs.versions.toml dosyasında bağımlılığı tanımlayın:
[versions]
androidx-viewmodel = 2.11.0
[libraries]
androidx-lifecycle-viewmodel = { module = "androidx.lifecycle:lifecycle-viewmodel", version.ref = "androidx-viewmodel" }
Ardından, yapıyı KMP modülünüzün build.gradle.kts dosyasına ekleyin ve bağımlılığı api olarak tanımlayın. 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 kitaplıklar ikili çerçeveye otomatik olarak aktarılmaz. API'ler dışa aktarılmazsa yalnızca paylaşılan kodda (iosMain veya commonMain kaynak grubundan) kullanırsanız ikili çerçeveden kullanılabilir. Bu durumda API'ler paket ön ekini 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 programlara dışa aktarma bölümünü inceleyin.
Deneyimi iyileştirmek için iOS ikili çerçevesini tanımladığınız build.gradle.kts dosyasındaki export kurulumunu kullanarak ViewModel bağımlılığını ikili çerçeveye aktarabilirsiniz. Bu sayede ViewModel API'lerine doğrudan Swift kodundan ve Kotlin kodundan erişilebilir:
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, masaüstünde varsayılan olarak kullanılamayabilen Dispatchers.Main.immediate ile ilişkilendirilir. 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 bilgi için Dispatchers.Main belgelerini inceleyin.
commonMain veya androidMain'dan ViewModel'i kullanın
ViewModel sınıfını paylaşılan commonMain veya androidMain kaynak grubunda kullanmak için özel bir şart yoktur. Tek dikkat etmeniz gereken, platforma özel API'leri kullanamayacağınız ve bunları soyutlamanız gerektiğidir. Örneğin, ViewModel kurucu parametresi olarak Android Application kullanıyorsanız bu API'yi soyutlayarak API'den geçiş yapmanız gerekir.
Platforma özel kodun nasıl kullanılacağı hakkında daha fazla bilgiyi Kotlin Multiplatform'da platforma özel kod sayfasında bulabilirsiniz.
Örneğin, aşağıdaki snippet'te commonMain içinde tanımlanan fabrikasıyla birlikte bir ViewModel sınıfı yer alır:
// 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'dan ViewModel'i kullanma
Android'de ViewModel yaşam döngüsü otomatik olarak yönetilir ve kapsamı bir ComponentActivity, Fragment, NavBackStackEntry (Navigation 2) veya rememberViewModelStoreNavEntryDecorator (Navigation 3) ile sınırlıdı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.
Genel türlerle ilgili yardımcı olacak bir işlev oluşturma
Genel bir ViewModel örneği oluşturmak için Android'de sınıf referansı yansıma özelliği kullanılır. Objective-C jenerikleri, Kotlin veya Swift'in tüm özelliklerini desteklemediğinden Swift'ten jenerik türde bir ViewModel'i doğrudan alamazsınız.
Bu sorunu çözmek için jenerik türü yerine ObjCClass kullanacak bir yardımcı işlev oluşturabilir ve ardından örneklendirilecek 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 iletebilen T.self işlevini kullanabilirsiniz.
ViewModel kapsamını SwiftUI yaşam döngüsüne bağlama
Sonraki adım, ObservableObject ve ViewModelStoreOwner arayüzlerini (protokollerini) uygulayan bir IosViewModelStoreOwner oluşturmaktır. ObservableObject, bu sınıfı SwiftUI kodunda @StateObject olarak kullanabilmek için gereklidir:
// 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 olanak tanır.
IosViewModelStoreOwner kullanan ekranın başlatılması kaldırıldığında ve deinit çağrıldığında bu ViewModel'lerin yaşam döngüsü temizlenir. İlk başlatmayı geri alma hakkında daha fazla bilgiyi resmi belgelerde bulabilirsiniz.
Bu noktada, SwiftUI görünümünde IosViewModelStoreOwner öğesini @StateObject olarak örneklendirebilir ve ViewModel'i 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 Çok Platformlu'da kullanılamaz
Android'de kullanılabilen API'lerden bazıları Kotlin Multiplatform'da kullanılamaz.
Hilt ile entegrasyon
Hilt, Kotlin Multiplatform projelerinde kullanılamadığından commonMain sourceSet'te @HiltViewModel ek açıklamasıyla ViewModel'leri 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 eş yordam akışlarını gözlemleme doğrudan desteklenmez. Ancak bu özelliğe izin vermek için KMP-NativeCoroutines veya SKIE kitaplığını kullanabilirsiniz.