SwiftUI/组合监听 ObservableObject 中的多个发布者

Posted

技术标签:

【中文标题】SwiftUI/组合监听 ObservableObject 中的多个发布者【英文标题】:SwiftUI/ Combine Listen to multiple publishers inside an ObservableObject 【发布时间】:2021-06-20 15:23:40 【问题描述】:

我有一个ObservableObject,里面还有两个ObservableObjects。这两个ObservableObject 中的每一个在内部都有一个或多个@Published 属性。我的解决方案有效,但我 100% 确定必须有另一种方法。我必须复制/粘贴所有内容才能使其正常工作。我尝试了两个将这两个发布者放在视图中,但是我需要将很多参数传递给子视图。有什么想法可以简化吗?

class PackageService: ObservableObject 
    @Published var package: Package
    @Published var error: Error?
    
    @Published var distance: Double?
    @Published var expectedTravelTime: String?
    
    @Published var amount: Int = 0
    @Published var cost: Double = 0
    
    @Published var annotations = [MKPointAnnotation]()

    @Published var isCalculating = true
    @Published var route: MKRoute?
    
    var amountService = AmountService()
    var routeService = RouteService()
    
    private var cancellables = Set<AnyCancellable>()
    
    init(package: Package) 
        self.package = package
        routeService.calcDirections(source: package.source.toCLLocationCoordinate2D, destination: package.destination.toCLLocationCoordinate2D)
        
        routeService.$route.sink  [self] route in
            self.route = route
            
            amountService.calc(distance: route?.distance)
            calc(route: route)
        
        .store(in: &cancellables)
        
        routeService.$isCalculating.sink  [self] isCalculating in
            self.isCalculating = isCalculating
        
        .store(in: &cancellables)
        
        
        routeService.$error.sink  [self] error in
            self.error = error
        
        .store(in: &cancellables)
        
        amountService.$amount.sink  [self] amount in
            self.amount = amount
        
        .store(in: &cancellables)
        
        amountService.$cost.sink  [self] cost in
            self.cost = cost
        
        .store(in: &cancellables)
        
        amountService.$error.sink  [self] error in
            self.error = error
        
        .store(in: &cancellables)
    

【问题讨论】:

【参考方案1】:

似乎PackageService 只是另外两个服务的薄包装。

使用计算属性而不是订阅和发布每个属性的更改是有意义的:

var isCalculating: Bool  routeService.isCalculating 

var amount: Int  amountService.amount 

// etc...

并订阅一次以通知视图的更改:

routeService.objectWillChange
            .merge(with: amountService.objectWillChange)
            .sink(receiveValue: self.objectWillChange.send)
            .store(in: &cancellables)

另外,考虑将各种属性打包在一个数据模型中,例如:

struct AmountModel 
   var amount: Int
   var cost: Double

并让AmountService 拥有一个属性。这可以是 Result&lt;AmountModel, Error&gt; 来捕获数据或错误,或者,如果需要,可以将 Error 作为属性包含在内。

【讨论】:

很好的答案,我知道它可以简化。 但是我如何对 routeService 内部的变化做出反应,因为当 routeService 中的属性更新时,我需要触发 amountService.calc(distance: route?.distance) @M1X,如果您需要执行一些与 SwiftUI 无关的操作,那么是的,您可以使用 Combine 进行观察,例如route.$distance.sink... 但您询问了有关在 SwiftUI 中观察的问题。 SwiftUI 的所有需求(这也回答了您的第二条评论的问题)是知道发生了一些变化(请参阅我的回答中的 .sink(receiveValue: self.objectWillChange.send)),然后重新加载视图。所以,你可以做.onChange(packageSvc.distance) packageSvc.recalcAmount() 好的,我想我明白了.sink(receiveValue: self.objectWillChange.send) 通知视图。 .onChange(packageSvc.distance) packageSvc.recalcAmount() 捕捉视图中的变化。如果我想执行一些操作,我需要使用sink 同样奇怪,但即使没有 .sink(receiveValue: self.objectWillChange.send),返回 @Published 的计算属性也会反映视图的更改

以上是关于SwiftUI/组合监听 ObservableObject 中的多个发布者的主要内容,如果未能解决你的问题,请参考以下文章

SwiftUI + Firebase - 监听器不监听变化?

SwiftUI如何监听WKWebView加载网页的进度

Firebase 和 swiftUI,监听实时更新奇怪的行为怪异

SwiftUI 包装 UICollectionView 并使用组合布局

SwiftUI:视图组合的代码重用

SwiftUI:监听我的数据中的变化,让其他视图使用更新的数据