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<AmountModel, Error>
来捕获数据或错误,或者,如果需要,可以将 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 - 监听器不监听变化?
Firebase 和 swiftUI,监听实时更新奇怪的行为怪异