You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 12:26:17