使用 Combine 设置发布者,同时满足对非零变量初始值的要求
Using Combine to set up Publishers and while also satisfying requirement for initial values on non-nil variables
我对反应式编程概念比较陌生,我正在尝试构建一个简单的视图模型来更新 @Published Bool
用于保持 UI 使用 Swift [=38] 更新的值=].
此特定模型根据 WatchConnectivity
框架中随时间变化的其他值来设置这些 Bool
值。
尽管这是一个简单的示例并且它正在运行,但我觉得我错过了减少冗余的机会。
具体来说,我感觉很奇怪,我在后面使用Publishers.CombineLatest
和Publishers.CombineLatest3
时重复了用于设置appNotInstalled
和complicationNotInstalled
初始值的逻辑.
尽管初始值是通过发布者传递的,所以他们通过 CombineLatest
管道并设置初始值,但随意将发布变量设置为 true
或 false
,但编译器让我在某处为它们设置初始值。
如果我不设置初始值,我会收到 Variable 'self.appNotInstalled' used before being initialized
错误。
有没有一种方法可以避免设置初始值而不将其设为 nil,或者有其他方法可以避免重复用于确定其值的逻辑?
这是我的工作代码:
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)
}
}
我对反应式编程概念比较陌生,我正在尝试构建一个简单的视图模型来更新 @Published Bool
用于保持 UI 使用 Swift [=38] 更新的值=].
此特定模型根据 WatchConnectivity
框架中随时间变化的其他值来设置这些 Bool
值。
尽管这是一个简单的示例并且它正在运行,但我觉得我错过了减少冗余的机会。
具体来说,我感觉很奇怪,我在后面使用Publishers.CombineLatest
和Publishers.CombineLatest3
时重复了用于设置appNotInstalled
和complicationNotInstalled
初始值的逻辑.
尽管初始值是通过发布者传递的,所以他们通过 CombineLatest
管道并设置初始值,但随意将发布变量设置为 true
或 false
,但编译器让我在某处为它们设置初始值。
如果我不设置初始值,我会收到 Variable 'self.appNotInstalled' used before being initialized
错误。
有没有一种方法可以避免设置初始值而不将其设为 nil,或者有其他方法可以避免重复用于确定其值的逻辑?
这是我的工作代码:
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)
}
}