I have an ObservableObject
which inside has two more ObservableObjects
. Each of these two ObservableObject
has one or more @Published
properties inside. My solution works but I'm 100% sure there must be another way. I have to copy/ paste everything in order to make it work. I tried two put those two publishers in the View but then I need to pass a lot of params to the child view. Any ideas how to simplify this?
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)
}
Seems like the PackageService
is just a thin wrapper around two other services.
It would make sense to use computed properties instead of subscribing and publishing a change for each property:
var isCalculating: Bool { routeService.isCalculating }
var amount: Int { amountService.amount }
// etc...
and to subscribe once in order to signal changes to the view:
routeService.objectWillChange
.merge(with: amountService.objectWillChange)
.sink(receiveValue: self.objectWillChange.send)
.store(in: &cancellables)
Also, consider packaging the various properties in a single data model, e.g.:
struct AmountModel {
var amount: Int
var cost: Double
}
and have the AmountService
have a single property. This could be a Result<AmountModel, Error>
to capture either the data or the error, or, if you need, include the Error
as a property.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With