swift - 使用 Combine 设置 Publishers,同时满足对非 nil 变量初始值的要求
问题描述
我对反应式编程概念相对较新,我正在尝试构建一个简单的视图模型来更新@Published Bool
用于保持 UI 与 SwiftUI 更新的值。
Bool
这个特定模型根据WatchConnectivity
框架中的其他值随着时间的推移而变化来设置这些值。
尽管这是一个简单的示例并且它正在工作,但我觉得我错过了减少冗余的机会。
具体来说,当我使用appNotInstalled
and时,我重复了用于设置初始值的逻辑,这感觉很奇怪。complicationNotInstalled
Publishers.CombineLatest
Publishers.CombineLatest3
即使初始值是通过发布者传递的,所以它们会通过管道并设置初始值,将发布的变量随意设置为orCombineLatest
感觉是错误的,但是编译器让我在某处为它们设置初始值。true
false
如果我没有设置初始值,我会收到Variable 'self.appNotInstalled' used before being initialized
错误消息。
有没有一种方法可以避免设置初始值而不使它们为零,或者另一种方法可以避免重复用于确定其值的逻辑?
这是我的工作代码:
class WatchConnectivityModel: ObservableObject {
// values used to show/hide UI
@Published var appNotInstalled: Bool
@Published var complicationNotInstalled: Bool
private var cancellables: [AnyCancellable] = []
init() {
// initialize based on the values of everything at class init
let activated = WCSession.default.activationState == .activated
let appInstalled = WCSession.default.isWatchAppInstalled
let complicationInstalled = WCSession.default.isComplicationEnabled
appNotInstalled = !(activated && appInstalled)
complicationNotInstalled = activated && appInstalled && !complicationInstalled
// set up the publishers for any changes
let activationStatePublisher = WCSession.default.publisher(for: \.activationState)
let isWatchAppInstalledPublisher = WCSession.default
.publisher(for: \.isWatchAppInstalled)
let isComplicationEnabledPublisher = WCSession.default
.publisher(for: \.isComplicationEnabled)
// set up assignment of appNotInstalled for changes
Publishers.CombineLatest(activationStatePublisher.removeDuplicates(),
isWatchAppInstalledPublisher.removeDuplicates())
.map { (state, installed) in
// repeated logic from above
return !(state == .activated && installed)
}.receive(on: RunLoop.main)
.assign(to: \.appNotInstalled, on: self)
.store(in: &cancellables)
// set up assignment of complicationNotInstalled for changes
Publishers.CombineLatest3(activationStatePublisher.removeDuplicates(),
isWatchAppInstalledPublisher.removeDuplicates(),
isComplicationEnabledPublisher.removeDuplicates())
.map { (state, appInstalled, complicationInstalled) in
// repeated logic again
return state == .activated && appInstalled && !complicationInstalled
}.receive(on: RunLoop.main)
.assign(to: \.complicationNotInstalled, on: self)
.store(in: &cancellables)
}
}
解决方案
我为每个创建了一个新的初始化结构,其中只包含每个的逻辑。每当我设置初始值或更新已发布的变量时,我都会使用它来避免重复逻辑。如果有人对此有更好的解决方案,我愿意接受其他可能性。
import Foundation
import Combine
import WatchConnectivity
class WatchConnectivityModel: ObservableObject {
// values used to show/hide UI
@Published var appNotInstalled: Bool
@Published var complicationNotInstalled: Bool
private struct AppNotInstalled {
let value: Bool
init(_ activationState: WCSessionActivationState,
_ appInstalled: Bool) {
value = !(activationState == .activated && appInstalled)
}
}
private struct ComplicationNotInstalled {
let value: Bool
init(_ activationState: WCSessionActivationState,
_ appInstalled: Bool,
_ complicationInstalled: Bool) {
value = activationState == .activated && appInstalled && !complicationInstalled
}
}
private var cancellables: [AnyCancellable] = []
init() {
// initilize based on the current values
let state = WCSession.default.activationState
let appInstalled = WCSession.default.isWatchAppInstalled
let complicationInstalled = WCSession.default.isComplicationEnabled
appNotInstalled = AppNotInstalled(state,
appInstalled).value
complicationNotInstalled = ComplicationNotInstalled(state,
appInstalled,
complicationInstalled).value
// set up the publishers
let activationStatePublisher = WCSession.default
.publisher(for: \.activationState)
let isWatchAppInstalledPublisher = WCSession.default
.publisher(for: \.isWatchAppInstalled)
let isComplicationEnabledPublisher = WCSession.default
.publisher(for: \.isComplicationEnabled)
// set up assignment of appNotInstalled
Publishers.CombineLatest(activationStatePublisher.removeDuplicates(),
isWatchAppInstalledPublisher.removeDuplicates())
.map { (state, installed) in
return AppNotInstalled(state, installed).value
}.receive(on: RunLoop.main)
.assign(to: \.appNotInstalled,
on: self)
.store(in: &cancellables)
// set up assignment of complicationNotInstalled
Publishers.CombineLatest3(activationStatePublisher.removeDuplicates(),
isWatchAppInstalledPublisher.removeDuplicates(),
isComplicationEnabledPublisher.removeDuplicates())
.map { (state, appInstalled, complicationInstalled) in
return ComplicationNotInstalled(state, appInstalled, complicationInstalled).value
}.receive(on: RunLoop.main)
.assign(to: \.complicationNotInstalled,
on: self)
.store(in: &cancellables)
}
}
推荐阅读
- python - 在 Python 中多处理一个 for 循环
- powershell - 以固定长度的科学形式将浮点数转换为十进制数
- vb.net - SVG 转换的奇怪透明度问题
- angular - Angular DOMSanitizationService 未定义
- asp.net-core - HotChocolate GraphQL 在运行时添加类型扩展或模式
- reactjs - 开玩笑 setSystemTime 不适用于全局范围
- swiftui - 触发时查看不动画
- r - 计算不同时间点动物之间的距离
- python - 通过删除子字符串获得的最大成本
- zsh - 在交互式 shell 中关闭函数参数中的 ZSH glob 扩展