ViewModel AndroidX berfungsi sebagai jembatan, yang menetapkan kontrak yang jelas antara logika bisnis bersama dan komponen UI Anda. Pola ini membantu memastikan konsistensi data di seluruh platform, sekaligus memungkinkan UI disesuaikan untuk tampilan unik setiap platform. Anda dapat melanjutkan pengembangan UI dengan Jetpack Compose di Android dan SwiftUI di iOS.
Baca selengkapnya manfaat penggunaan ViewModel dan semua fitur dalam dokumentasi utama untuk ViewModel.
Menyiapkan dependensi
Untuk menyiapkan ViewModel KMP di project Anda, tentukan dependensi dalam
file libs.versions.toml
:
[versions]
androidx-viewmodel = 2.9.3
[libraries]
androidx-lifecycle-viewmodel = { module = "androidx.lifecycle:lifecycle-viewmodel", version.ref = "androidx-viewmodel" }
Kemudian, tambahkan artefak ke file build.gradle.kts
untuk modul KMP Anda
dan deklarasikan dependensi sebagai api
, karena dependensi ini akan diekspor ke
framework biner:
// 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)
}
Mengekspor ViewModel API untuk akses dari Swift
Secara default, library apa pun yang Anda tambahkan ke codebase tidak akan otomatis
diekspor ke framework biner. Jika API tidak diekspor, API hanya tersedia dari framework biner jika Anda menggunakannya dalam kode bersama
(dari set sumber iosMain
atau commonMain
). Dalam hal ini, API akan berisi awalan paket, misalnya class ViewModel
akan tersedia sebagai class Lifecycle_viewmodelViewModel
. Lihat mengekspor dependensi ke biner untuk mengetahui informasi selengkapnya tentang cara mengekspor dependensi.
Untuk meningkatkan pengalaman, Anda dapat mengekspor dependensi ViewModel ke framework biner menggunakan penyiapan export
di file build.gradle.kts
tempat Anda menentukan framework biner iOS, yang membuat API ViewModel dapat diakses langsung dari kode Swift seperti dari kode 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"
}
}
(Opsional) Menggunakan viewModelScope
di Desktop JVM
Saat menjalankan coroutine di ViewModel, properti viewModelScope
terikat ke
Dispatchers.Main.immediate
, yang mungkin tidak tersedia di desktop secara
default. Agar berfungsi dengan benar, tambahkan dependensi kotlinx-coroutines-swing
ke project Anda:
// Optional if you use JVM Desktop
desktopMain.dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-swing:[KotlinX Coroutines version]")
}
Lihat dokumentasi Dispatchers.Main
untuk mengetahui detail selengkapnya.
Menggunakan ViewModel dari commonMain
atau androidMain
Tidak ada persyaratan khusus untuk menggunakan class ViewModel di commonMain
bersama, atau dari sourceSet androidMain
. Satu-satunya pertimbangan adalah Anda tidak dapat menggunakan API khusus platform dan Anda harus mengabstraksikannya. Misalnya, jika Anda menggunakan Application
Android sebagai parameter konstruktor ViewModel, Anda harus bermigrasi dari API ini dengan mengabstraksikannya.
Informasi selengkapnya tentang cara menggunakan kode khusus platform tersedia di kode khusus platform di Multiplatform Kotlin.
Misalnya, dalam cuplikan berikut adalah class ViewModel dengan factory-nya,
yang ditentukan dalam 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()
Kemudian, dalam kode UI, Anda dapat mengambil ViewModel seperti biasa:
// androidApp/ui/MainScreen.kt @Composable fun MainScreen( viewModel: MainViewModel = viewModel( factory = mainViewModelFactory, ), ) { // observe the viewModel state }
Menggunakan ViewModel dari SwiftUI
Di Android, siklus proses ViewModel ditangani dan dicakup secara otomatis ke
ComponentActivity
, Fragment
, NavBackStackEntry
(Navigation 2), atau
rememberViewModelStoreNavEntryDecorator
(Navigation 3). Namun, SwiftUI di iOS tidak memiliki padanan bawaan untuk ViewModel AndroidX.
Untuk membagikan ViewModel dengan aplikasi SwiftUI, Anda perlu menambahkan beberapa kode penyiapan.
Membuat fungsi untuk membantu generik
Membuat instance ViewModel generik menggunakan fitur refleksi referensi class di Android. Karena generik Objective-C tidak mendukung semua fitur Kotlin atau Swift, Anda tidak dapat langsung mengambil ViewModel jenis generik dari Swift.
Untuk membantu mengatasi masalah ini, Anda dapat membuat fungsi helper yang akan menggunakan
ObjCClass
, bukan jenis generik, lalu menggunakan getOriginalKotlinClass
untuk mengambil class ViewModel yang akan dibuat instance-nya:
// 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] }
Kemudian, saat ingin memanggil fungsi dari Swift, Anda dapat menulis fungsi generik berjenis T : ViewModel
dan menggunakan T.self
, yang dapat meneruskan ObjCClass
ke fungsi resolveViewModel
.
Menghubungkan cakupan ViewModel ke Siklus Proses SwiftUI
Langkah selanjutnya adalah membuat IosViewModelStoreOwner
yang menerapkan antarmuka (protokol) ObservableObject
dan ViewModelStoreOwner
. Alasan
ObservableObject
adalah agar dapat menggunakan class ini sebagai @StateObject
dalam kode 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() } }
Pemilik ini memungkinkan pengambilan beberapa jenis ViewModel, seperti di Android.
Siklus proses ViewModel tersebut akan dihapus saat layar yang menggunakan
IosViewModelStoreOwner
di-deinisialisasi dan memanggil deinit
. Anda dapat mempelajari lebih lanjut deinisialisasi di dokumentasi resmi.
Pada tahap ini, Anda cukup membuat instance IosViewModelStoreOwner
sebagai
@StateObject
di Tampilan SwiftUI dan memanggil fungsi viewModel
untuk mengambil
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 } }
Tidak Tersedia di Multiplatform Kotlin
Beberapa API yang tersedia di Android tidak tersedia di Kotlin Multiplatform.
Integrasi dengan Hilt
Karena Hilt tidak tersedia untuk project Multiplatform Kotlin,
Anda tidak dapat langsung menggunakan ViewModel dengan anotasi @HiltViewModel
di
sourceSet commonMain
. Dalam hal ini, Anda perlu menggunakan framework DI alternatif, misalnya, Koin, kotlin-inject, Metro, atau Kodein. Anda dapat menemukan semua framework DI yang kompatibel dengan
Kotlin Multiplatform di klibs.io.
Mengamati Alur di SwiftUI
Mengamati Flow coroutine di SwiftUI tidak didukung secara langsung. Namun, Anda dapat menggunakan library KMP-NativeCoroutines atau SKIE untuk mengizinkan fitur ini.