swift - 如何在 Swift 中访问 KMMShared MutableStateFlow 对象

标签 swift kotlin swiftui kotlin-multiplatform

我有一个带有 MutableStateFlow 的 KmmShared 单例类,它可以在 Android 上正常工作。我需要知道访问此 Singleton 类及其 MutableStateFlow 变量的最佳方法,并在 SwiftUI 应用程序中进行更新。

class MySingeltonClass {
    private val _selectedType: MutableStateFlow<MyEnum> = MutableStateFlow(getUserPrefsType())
    var selectedType = _selectedType.asStateFlow()

    private fun getUserPrefsType() : MyEnum {
        // Get Enum from DB
    }

    fun setType(scheme: MyEnum) {
        // Set Enum in DB
        _selectedType.value = scheme
    }

    companion object {
        private lateinit var instance: MySingeltonClass
        fun getInstance(): MySingeltonClass {
            if (!this::instance.isInitialized) {
                instance = MySingeltonClass()
            }
            return instance
        }

    }
}

enum class MyEnum {
    TYPEA, TYPEB, TYPEC;
}

如果我可以拥有一个带有 @Published var selectedType@ObservedObject private var selectedTypeMySingeltonWrapperClass ,我正在尝试在 Swift 中实现。

最佳答案

这是一个有点棘手的话题。 SwiftUI 应用程序中 Flow/StateFlow 的主要问题在于 Kotlin <-> Swift 互操作性。因为它有几个限制:

更好理解的好文章:one , two

有几种可用的解决方法:

1。将 Flow 封装在 Kotlin class 中。您可以在 official KMM sample repo 中找到此技术的绝佳示例。 .

首先,author introduces new class CFlow:

fun interface Closeable {
    fun close()
}

class CFlow<T: Any> internal constructor(private val origin: Flow<T>) : Flow<T> by origin {
    fun watch(block: (T) -> Unit): Closeable {
        val job = Job()

        onEach {
            block(it)
        }.launchIn(CoroutineScope(Dispatchers.Main + job))

        return Closeable { job.cancel() }
    }
}

internal fun <T: Any> Flow<T>.wrap(): CFlow<T> = CFlow(this)

然后uses it in SwiftUI :


class ObservableFeedStore: ObservableObject {
    @Published public var state: FeedState =  FeedState(progress: false, feeds: [], selectedFeed: nil)
    
    var stateWatcher : Closeable?

    init(store: FeedStore) {
        stateWatcher = self.store.watchState().watch { [weak self] state in
            self?.state = state
        }
    }
    
    deinit {
        stateWatcher?.close()
    }
}

2。使用特殊的库,例如KMP-NativeCoroutines .

当您设置此库时,您可以为您的 Kotlin Flow/StateFlow 创建 Swift AnyPublisher:

let publisher = createPublisher(for: yourFlow)

3。不要使用Flow/StateFlow。您可以使用简单的替代方法。看看Decompose project它是 class .

abstract class Value<out T : Any> {
    abstract val value: T
    abstract fun subscribe(observer: (T) -> Unit)
    abstract fun unsubscribe(observer: (T) -> Unit)
}

您仍然可以非常轻松地在 Compose 应用程序中使用它来获取Statesample from Decompose :

@Composable
fun <T : Any> Value<T>.subscribeAsState(policy: SnapshotMutationPolicy<T> = structuralEqualityPolicy()): State<T> {
    val state = remember(this, policy) { mutableStateOf(value, policy) }

    DisposableEffect(this) {
        val observer: (T) -> Unit = { state.value = it }

        subscribe(observer)

        onDispose {
            unsubscribe(observer)
        }
    }

    return state
}

并且在 SwiftUI 中您可以使用 ObservableValue helper from Decompose :

public class ObservableValue<T : AnyObject> : ObservableObject {
    private let observableValue: Value<T>

    @Published
    var value: T

    private var observer: ((T) -> Void)?
    
    init(_ value: Value<T>) {
        observableValue = value
        self.value = observableValue.value
        observer = { [weak self] value in self?.value = value }
        observableValue.subscribe(observer: observer!)
    }

    deinit {
        observableValue.unsubscribe(observer: self.observer!)
    }
}

关于swift - 如何在 Swift 中访问 KMMShared MutableStateFlow 对象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/76144811/

相关文章:

swift - 我如何处理拖动到 SwiftUI 中的停靠栏图标?

ios - 如何仅在 swiftUI 中将拖动手势限制到特定帧

swift - Xcode/swift : How to add extra argument to a bash execution

swift - 远程图像内容错误快速错误

java - Kotlin Array 的 toList 和 asList 有何不同?

android - Firebase Firestore Android无法反序列化对象

ios - 适用于 twilio 的 AlamoFire

swift - 如何创建要在 SceneKit 中使用的 DAE 文件?

Android Studio 2.3.2 和 Kotlin 1.1.2-4 + 数据绑定(bind)

ios - 如何在 SwiftUI 中管理 AVPlayer 状态