I am trying to create a function that will return me a struct constructed from two publishers within a chain of Combine calls, something like this pseudocode
struct UpdatedPilotsInfo {
let pilots: [JSON]
let squad: JSON
}
func buildInfoIdeal() {
func processPilots(squad: JSON) -> AnyPublisher<[JSON] , XWSImportError> { ... }
func loadSquad() -> AnyPublisher<JSON, XWSImportError> { ... }
let squadInfoPublisher: AnyPublisher<(JSON, [JSON]), XWSImportError> = JSONSection
.SquadService_XWSImport_New
.SquadService
.loadSquad() // AnyPublisher<JSON, XWSImportError>
.flatMap(processPilots) // AnyPublisher<[JSON], XWSImportError>
.??? // AnyPublisher<(JSON, [JSON]), XWSImportError>
.map{ tuple -> UpdatedPilotsInfo in
return UpdatedPilotsInfo(squad: tuple.0, pilots: tuple.1)
}.eraseToAnyPublisher()
}
My issue is that I don’t know how I can make an upstream publisher created at the top of the chain (loadSquad
) available further down the chain. In the example above, I pass the output of the loadSquad()
to processPilots()
and I’d like to build a UpdatedPilotsInfo
from the results of loadSquad()
and processPilots()
. I think that I can accomplish this by doing the following, but I’d like to have a simple chain instead of all this code:
func buildInfo() -> AnPublisher<UpdatedPilotsInfo, XWSImportError> {
func processPilots(squad: JSON) -> AnyPublisher<[JSON] , XWSImportError> { ... }
func loadSquad() -> AnyPublisher<JSON, XWSImportError> { ... }
let squadPublisher: AnyPublisher<JSON, XWSImportError> = JSONSection
.SquadService_XWSImport_New
.SquadService
.loadSquad() // AnyPublisher<JSON, XWSImportError>
.eraseToAnyPublisher()
let updatedPilots: AnyPublisher<[JSON], XWSImportError> = squadPublisher
.flatMap{ [weak self] squad -> AnyPublisher<[JSON], XWSImportError in
let s = self
return s.processPilots(squad)
}
.eraseToAnyPublisher()
let zip = Publishers.Zip(squadPublisher, updatedPilots)
.eraseToAnyPublisher() // AnyPublisher<(JSON, [JSON]), XWSImportError>
return zip.map{ tuple -> UpdatedPilotsInfo in
return UpdatedPilotsInfo(squad: tuple.0, pilots: tuple.1)
}.eraseToAnyPublisher()
}
Is this something I can accomplish with an existing operator or do I need to create a custom operator?
Keep in mind that .flatMap { Just($0) }
is exactly the same as .map { $0 }
. And both are a no-op.
This means you can pass down the parameter using just while combining it with some other publisher that does some actual work.
loadSquad
.flatMap { Publishers.Zip(Just($0).setFailureType(to: Error.self), processPilots($0)) }
.map { UpdatedPilotsInfo(squad: $0.0, pilots: $0.1) }
.eraseToAnyPublisher()
So with the above, we are passing the response from loadSquad
along with the response from processPilots
inside the flatMap
.