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 terus mengembangkan UI dengan Jetpack Compose di Android dan SwiftUI di iOS.
Baca selengkapnya tentang manfaat menggunakan 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.11.0
[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 tidak diekspor, API hanya akan 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. Periksa mengekspor dependensi ke
biner untuk mengetahui informasi selengkapnya tentang mengekspor
dependensi.
Untuk meningkatkan pengalaman, Anda dapat mengekspor dependensi ViewModel ke framework biner menggunakan penyiapan export dalam file build.gradle.kts tempat Anda menentukan framework biner iOS, yang membuat ViewModel API dapat diakses langsung dari kode Swift sama 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 JVM Desktop
Saat menjalankan coroutine di ViewModel, properti viewModelScope terkait dengan
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 perlu 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, ada class ViewModel dengan factory-nya,
yang ditentukan di 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 otomatis ditangani dan dicakup ke
ComponentActivity, Fragment, NavBackStackEntry (Navigation 2), atau
rememberViewModelStoreNavEntryDecorator (Navigation 3). Namun, SwiftUI di iOS tidak memiliki padanan bawaan untuk ViewModel AndroidX.
Untuk membagikan ViewModel ke 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 dari jenis generik dari Swift.
Untuk membantu mengatasi masalah ini, Anda dapat membuat fungsi bantuan 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
dari jenis T : ViewModel dan menggunakan T.self, yang dapat meneruskan
ObjCClass ke fungsi resolveViewModel.
Menghubungkan cakupan ViewModel ke Siklus Proses SwiftUI
Langkah berikutnya adalah membuat IosViewModelStoreOwner yang mengimplementasikan
antarmuka (protokol) ObservableObject dan ViewModelStoreOwner. Alasan
untuk 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 dihapus saat layar yang menggunakan
IosViewModelStoreOwner di-deinisialisasi dan memanggil deinit. Anda dapat mempelajari
lebih lanjut deinitialization di
dokumentasi resmi.
Pada tahap ini, Anda dapat 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 beberapa framework DI
alternatif, misalnya, Koin,
kotlin-inject, Metro, atau
Kodein. Anda dapat menemukan semua framework DI yang berfungsi dengan
Kotlin Multiplatform di klibs.io.
Mengamati Flow di SwiftUI
Mengamati Flow coroutine di SwiftUI tidak didukung secara langsung. Namun, Anda dapat menggunakan library KMP-NativeCoroutines atau SKIE untuk mengizinkan fitur ini.