Swift中如何对Combine订阅者实现条件性的变更发布功能?
解决方案
你之前方案失效的核心原因:ObservableObject 会自动为类中所有被@Published修饰的存储属性合成自动发布逻辑,只要属性值变更就会自动触发objectWillChange.send(),不受你自定义的willSet逻辑影响。你手动修改的版本如果还能收到事件,大概率是没有移除属性上的@Published修饰符。
最优实现:自定义条件发布属性包装器
通过自定义@ConditionallyPublished属性包装器可以完全消除样板代码,仅保留单个存储数据源,用法和原生@Published几乎一致:
首先定义属性包装器:
import Combine @propertyWrapper struct ConditionallyPublished<Value> { private let shouldPublish: () -> Bool private var value: Value init(wrappedValue: Value, shouldPublish: @escaping @autoclosure () -> Bool) { self.value = wrappedValue self.shouldPublish = shouldPublish } var wrappedValue: Value { get { value } set { value = newValue guard shouldPublish() else { return } projectedValue.send(newValue) } } let projectedValue: PassthroughSubject<Value, Never> = .init() }
使用方式
在你的RequestStatus类中直接用该属性包装器修饰需要条件发布的属性即可:
class RequestStatus: ObservableObject { static let shared = RequestStatus() var shouldPublishChanges: Bool = false // 仅需一行修饰,无额外样板代码 @ConditionallyPublished(shouldPublish: shared.shouldPublishChanges) var offlineModeActive: Bool = false // 新增其他需要条件发布的属性无需额外逻辑 @ConditionallyPublished(shouldPublish: shared.shouldPublishChanges) var networkReachable: Bool = true private init() {} }
订阅逻辑和原生@Published完全一致
直接用$符号访问属性的发布器即可:
import Combine class ExampleClass { var offlineModeCancellable: AnyCancellable? init() { self.offlineModeCancellable = RequestStatus.shared.$offlineModeActive.sink(receiveValue: { offlineModeActive in // 仅当shouldPublishChanges为true时才会收到变更回调 // 执行业务逻辑 }) } }
方案优势
- 无样板代码:新增条件发布属性仅需添加一行修饰符
- 无数据同步问题:仅保留单一存储数据源,不存在双值不同步风险
- 兼容原有使用习惯:订阅逻辑和原生
@Published完全一致,改造成本极低
如果你需要兼容原来订阅objectWillChange的用法,只需要在属性包装器的set方法中,条件触发时同时调用所属对象的objectWillChange.send()即可。
内容的提问来源于stack exchange,提问作者Alex Marchant
相关产品推荐
相关产品推荐

