Question: Multiple interactor calls
AndriiTsok opened this issue · comments
We have a page with a non persistent data (we don't use AppState for this one).
It displays two types of information, let's say Product details and Company record.
We are fetching Product data through interactor call and binding its value to loadable binding state by onAppear event.
Once we got the product info data that contains company id - we are pulling the company record. Right now we are doing this simply by handling onAppear event on the UI elements that depends on Product record == .loaded(product).
I am wondering how would handle such a case using Combine. It works but I personally feel that that the code smells.
Would love to hear how would you handle such a case. Seeking to make to more cleaner.
Attaching a simple, minimal example: https://gist.github.com/AndriiTsok/e49a90f0aa648314b6ba0d35ef42fca0
I'd suggest reorganizing the Interactors in this example. There is a clear dependency between loading Product
and Company
, UI cannot do anything until both are loaded.
The job of the Interactor is not only to be a facade between UI and networking layer, but more importantly, it should provide a convenient API for the UI layer while hiding all the complexity of the business logic.
In terms of the responsibilities, Interactors do resemble classical Services in many use cases, and your example perfectly illustrates where they differ - although OrganizationInteractor
and ProductInteractor
are good names for entities with "Service" responsibilities, "Interactor" should be more pliable for the UI layer.
You just need to chain two networking requests - querying the product followed by loading the company, and that should look like a single transaction for the UI.
So I would introduce a ProductPageInteractor
with a single load
function that under the hood chains two networking calls from different Repositories
using flatMap
. Something like so:
fund load(data: LoadableSubject<(Product, Company)>, productId: String) {
let cancelBag = CancelBag()
data.wrappedValue.setIsLoading(cancelBag: cancelBag)
productWebRepository
.loadProduct(id: productId)
.flatMap { [companyWebRepository] product in
companyWebRepository
.loadCompany(id: product.companyId)
.map { (product, $0) }
}
.sinkToLoadable { data.wrappedValue = $0 }
.store(in: cancelBag)
}
Thank you @nalexn for your advice! I am closing this one.