KMM-ViewModel
一个允许您在 Android 和 iOS 之间共享 ViewModels 的库。
兼容性
该库的最新版本使用 Kotlin 版本 1.9.10
。
还提供了适用于较旧和/或预览版 Kotlin 版本的兼容性版本
版本 | 版本后缀 | Kotlin | 协程 | AndroidX 生命周期 |
---|---|---|---|---|
最新 | 无后缀 | 1.9.10 | 1.7.3 | 2.6.1 |
1.0.0-ALPHA-13 | 无后缀 | 1.9.0 | 1.7.3 | 2.6.1 |
1.0.0-ALPHA-10 | 无后缀 | 1.8.22 | 1.7.2 | 2.6.1 |
1.0.0-ALPHA-9 | 无后缀 | 1.8.21 | 1.7.1 | 2.5.1 |
1.0.0-ALPHA-8 | 无后缀 | 1.8.21 | 1.7.0 | 2.5.1 |
1.0.0-ALPHA-7 | 无后缀 | 1.8.21 | 1.6.4 | 2.5.1 |
1.0.0-ALPHA-6 | 无后缀 | 1.8.20 | 1.6.4 | 2.5.1 |
1.0.0-ALPHA-4 | 无后缀 | 1.8.10 | 1.6.4 | 2.5.1 |
1.0.0-ALPHA-3 | 无后缀 | 1.8.0 | 1.6.4 | 2.5.1 |
Kotlin
将库添加到您的共享 Kotlin 模块中
dependencies {
api("com.rickclephas.kmm:kmm-viewmodel-core:1.0.0-ALPHA-14")
}
并创建您的 ViewModels
import com.rickclephas.kmm.viewmodel.KMMViewModel
import com.rickclephas.kmm.viewmodel.MutableStateFlow
import com.rickclephas.kmm.viewmodel.stateIn
open class TimeTravelViewModel: KMMViewModel() {
private val clockTime = Clock.time
/**
* A [StateFlow] that emits the actual time.
*/
val actualTime = clockTime.map { formatTime(it) }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), "N/A")
private val _travelEffect = MutableStateFlow<TravelEffect?>(viewModelScope, null)
/**
* A [StateFlow] that emits the applied [TravelEffect].
*/
val travelEffect = _travelEffect.asStateFlow()
}
正如您可能注意到的,它与 Android ViewModel 没有多大区别。
最明显的区别是 KMMViewModel
的父类
- import androidx.lifecycle.ViewModel
+ import com.rickclephas.kmm.viewmodel.KMMViewModel
- open class TimeTravelViewModel: ViewModel() {
+ open class TimeTravelViewModel: KMMViewModel() {
除此之外,只有两个小小的差异。
第一个是 stateIn
的不同导入
- import kotlinx.coroutines.flow.stateIn
+ import com.rickclephas.kmm.viewmodel.stateIn
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), "N/A")
第二个是不同的 MutableStateFlow
构造函数
- import kotlinx.coroutines.flow.MutableStateFlow
+ import com.rickclephas.kmm.viewmodel.MutableStateFlow
- private val _travelEffect = MutableStateFlow<TravelEffect?>(null)
+ private val _travelEffect = MutableStateFlow<TravelEffect?>(viewModelScope, null)
这些细微差异将确保任何状态更改都传播到iOS。
注意:
viewModelScope
是对实际的CoroutineScope
的一种包装,可以通过ViewModelScope.coroutineScope
属性访问。
KMP-NativeCoroutines
强烈建议您使用来自 KMP-NativeCoroutines 的 @NativeCoroutinesState
注解,将您的 StateFlow
转换为 Swift 中的属性。
@NativeCoroutinesState
val travelEffect = _travelEffect.asStateFlow()
有关更多信息及安装说明,请查看 KMP-NativeCoroutines 的 README。
备选方案
或者,您也可以自己在 iOS 源集合中创建扩展属性。
val TimeTravelViewModel.travelEffectValue: TravelEffect?
get() = travelEffect.value
Android
按您对其他 Android 视图模型的方式使用视图模型。
class TimeTravelFragment: Fragment(R.layout.fragment_time_travel) {
private val viewModel: TimeTravelViewModel by viewModels()
}
注意:即将推出对 Jetpack Compose 的改进支持。
Swift
将 Swift 包添加到您的 Package.swift
文件中。
dependencies: [
.package(url: "https://github.com/rickclephas/KMM-ViewModel.git", from: "1.0.0-ALPHA-14")
]
或者在 Xcode 中添加,方法是在 File
> Add Packages...
中提供以下 URL:https://github.com/rickclephas/KMM-ViewModel.git
。
CocoaPods
如果您愿意,您也可以使用 CocoaPods 而不是 SPM。
pod 'KMMViewModelSwiftUI', '1.0.0-ALPHA-14'
创建一个包含以下内容的 KMMViewModel.swift
文件。
import KMMViewModelCore
import shared // This should be your shared KMM module
extension Kmm_viewmodel_coreKMMViewModel: KMMViewModel { }
然后您可以使用视图模型几乎就像它是一个 ObservableObject
一样。
只需使用视图模型特有的属性包装器和函数。
ObservableObject |
KMMViewModel |
---|---|
@StateObject |
@StateViewModel |
@ObservedObject |
@ObservedViewModel |
@EnvironmentObject |
@EnvironmentViewModel |
environmentObject(_:) |
environmentViewModel(_:) |
例如,要将 TimeTravelViewModel
作为 StateObject
使用
import SwiftUI
import KMMViewModelSwiftUI
import shared // This should be your shared KMM module
struct ContentView: View {
@StateViewModel var viewModel = TimeTravelViewModel()
}
您也可以在 Swift 中对视图模型进行子类化。
import Combine
import shared // This should be your shared KMM module
class TimeTravelViewModel: shared.TimeTravelViewModel {
@Published var isResetDisabled: Bool = false
}
子视图模型
如果你的 KMMViewModel
公开了子视图模型,则需要额外的逻辑。
首先确保使用 NativeCoroutinesRefinedState
注解而不是 NativeCoroutinesState
注解。
class MyParentViewModel: KMMViewModel() {
@NativeCoroutinesRefinedState
val myChildViewModel: StateFlow<MyChildViewModel?> = MutableStateFlow(null)
}
然后,应该创建一个 Swift 扩展属性,使用 childViewModel(_, at:)
函数
extension MyParentViewModel {
var myChildViewModel: MyChildViewModel? {
childViewModel(__myChildViewModel, at: \.__myChildViewModel)
}
}
这将防止你的 Swift 视图模型过早地被释放。
注意:对于包含视图模型的列表、集合和字典,有
childViewModels(_, at:)
。