KMMViewModelSwiftUI 1.0.0-ALPHA-21

KMMViewModelSwiftUI 1.0.0-ALPHA-21

Rick Clephas 维护。



KMMViewModelSwiftUI 1.0.0-ALPHA-21

  • Rick Clephas

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:)