SwiftUI 5中如何用Combine发布者绑定新Observable框架变量?
SwiftUI 5:Combine发布者与@Observable类的简洁绑定方案
SwiftUI 5引入@Observable包装器替代原有的ObservableObject协议,同时移除了开发中常用的@Published包装器。这直接导致原本Combine发布者绑定@Published属性的assign(to:)方法无法直接在新的Observable类上使用。
旧实现方式(基于ObservableObject + @Published)
原来的写法简洁直接,通过@Published标记属性后,Combine发布者可以直接绑定到属性的投影值:
class OldObservable: ObservableObject { let model = SomeModel() @Published var someValue: String? init() { setupSubscribers() } func setupSubscribers() { // someValuePublisher 示例为 PassthroughSubject<String?, Never>() model.someValuePublisher.assign(to:$someValue) } }
新实现的痛点(基于@Observable)
使用@Observable后,属性不再需要@Published标记,但原先的assign(to:)方法无法直接绑定属性的投影值$someValue:
@Observable class NewObservable { let model = SomeModel() var someValue: String? init() { setupSubscribers() } func setupSubscribers() { // 此写法不再生效 model.someValuePublisher.assign(to:$someValue) } }
目前常见的替代方案是使用assign(to:on:),但需要手动管理订阅者集合,代码更冗长,还存在循环引用风险:
var subscribers = Set<AnyCancellable>() func setupSubscribers() { model.someValuePublisher.assign(to: \.someValue, on: self).store(in: &subscribers) }
或者使用sink(receiveValue:),同样会增加代码量。
简洁解决方案
我们可以通过扩展Combine的Publisher协议,添加一个适配@Observable类的assign(to:on:)方法,既保留旧写法的简洁性,又避免循环引用:
扩展实现
import Combine import SwiftUI extension Publisher where Failure == Never { func assign<T: Observable>(to keyPath: ReferenceWritableKeyPath<T, Output>, on object: T) -> AnyCancellable { sink { [weak object] value in object?[keyPath: keyPath] = value } } }
使用方式
扩展后,写法和旧版几乎一致,同时通过弱引用避免循环引用:
@Observable class NewObservable { let model = SomeModel() private var subscribers = Set<AnyCancellable>() var someValue: String? init() { setupSubscribers() } func setupSubscribers() { model.someValuePublisher .assign(to: \.someValue, on: self) .store(in: &subscribers) } }
更轻量的写法
如果不想编写扩展,也可以直接用sink结合弱引用简化代码,效果一致:
func setupSubscribers() { model.someValuePublisher .sink { [weak self] value in self?.someValue = value } .store(in: &subscribers) }
内容的提问来源于stack exchange,提问作者zouritre
相关产品推荐
相关产品推荐

