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

如何使用SwiftUI新@Observable宏构建Combine管道?

使用@Observable宏构建的类搭建Combine管道的方案

针对@Observable类无法直接通过$属性名获取Combine Publisher的问题,有两种可行的解决方案,同时也可以保留原有的ObservableObject方案作为替代。

方案一:利用Observation框架的Combine扩展

@Observable宏生成的类自动遵循Observation.Observable协议,Combine框架为该协议提供了扩展方法,可以通过publisher(for:)获取特定属性的变化Publisher。

修改后的代码示例:

import SwiftUI
import Combine

@Observable private class WhiteSpaceTrimmer {
    var incomingText: String
    var outgoingText: String
    private var cancellables = Set<AnyCancellable>() // 存储Combine订阅,防止被销毁
    
    init(initialText: String = "") {
        self.incomingText = initialText
        self.outgoingText = initialText
        
        // 获取incomingText的变化Publisher
        self.publisher(for: \.incomingText)
            .debounce(for: .seconds(0.1), scheduler: DispatchQueue.main)
            .map { $0.trimmingCharacters(in: .whitespaces) }
            .assign(to: \.outgoingText, on: self)
            .store(in: &cancellables)
    }
}

// ContentView无需修改,保持原代码即可
struct ContentView: View {
    @State private var trimmer = WhiteSpaceTrimmer(initialText: "  	Hello World  ")
    var body: some View {
        VStack{
            TextField("Input", text: $trimmer.incomingText)
                .textFieldStyle(.roundedBorder)
            HStack {
                Text("Raw Input: \"\(trimmer.incomingText)\"")
                Spacer()
            }
            HStack {
                Text("Cleaned Output: \"\(trimmer.outgoingText)\"")
                Spacer()
            }
        }
        .padding()
    }
}

关键注意点:

  • 必须导入Combine框架
  • 需要维护Set<AnyCancellable>来存储订阅,否则Combine管道会在初始化完成后立即被销毁
  • publisher(for: \.属性名)方法会发出该属性每次变更后的最新值,行为与@Published的$属性名一致

方案二:属性观察者+定时器替代Combine(轻量场景)

如果你的需求仅涉及简单的防抖或值转换,也可以不依赖Combine,直接用属性观察者配合定时器实现:

@Observable private class WhiteSpaceTrimmer {
    var incomingText: String {
        didSet {
            // 每次输入时重置防抖定时器
            debounceTimer?.invalidate()
            debounceTimer = Timer.scheduledTimer(withTimeInterval: 0.1, repeats: false) { [weak self] _ in
                guard let self else { return }
                self.outgoingText = self.incomingText.trimmingCharacters(in: .whitespaces)
            }
        }
    }
    var outgoingText: String
    private var debounceTimer: Timer?
    
    init(initialText: String = "") {
        self.incomingText = initialText
        self.outgoingText = initialText.trimmingCharacters(in: .whitespaces)
    }
}

这种方式无需引入Combine,代码更轻量,但处理复杂的数据流操作(如多源合并、过滤、链式转换等)时,Combine的可读性和扩展性会更有优势。

保留ObservableObject的选择

如果你的Combine管道逻辑复杂,继续使用ObservableObject + @Published确实是更直接的方案。目前Apple并未弃用ObservableObject,该方案可以正常使用,直到@Observable的Combine集成进一步完善。

内容的提问来源于stack exchange,提问作者Curious Jorge

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 17:33:15