Search code examples
swiftreactive-programmingrx-swiftreactivexreactive

rxswift: how to get the Observable<Any> resulting in a combination of Observable<[Category]>?


I have some doubts in my approach. I have two type of Observables:

//I can fetch from the server the houses and save them in the database
func houses() -> Observable<[House]>

//Given a houseId, I can fetch it's details and save them in the database
func houseDetail(id: Int) -> Observable<Family>

I would like to do an Observable which first fetch all houses, and then fetch the families. What I did is something like that:

//I am an observable which, when I complete, all data are saved
func fetchAllHousesAndFamily() -> Observable<Any> {
  var allHousesObservables: [Observable] = []
  for house in houses {
    allHousesObservables.append(houseDetail(house.id))
  }
  return Observable.combineLatest(allHousesObservables)
}

But this for...it doesn't seem to be reactive style to me, and it seems like a hack because I don't know enough about rx operators.

Do you have the right way to do it in the rxworld ?

Thank you


Solution

  • To get all family from the result of houses, you will want to use the flatMap operator. flatMap accepts a closure with signature T -> Observable<U>, so, in our specific example, it will be a function of type [House] -> Observable<Family>.

    houses().flatMap { houses in
      let details: [Observable<Family>] = houses.map { houseDetail($0.id) } // [1]
    
      return Observable.from(details).merge() // [2]
    }
    
    • [1]: We are mapping an array of house to an array of Observable<Family>.
    • [2]: from(_:) converts [Observable<Family>] to Observable<Observable<Family>>. merge() then transform it to Observable<Family>

    We now have an observable that will emit one next event for each house with its family details.

    If we'd prefer to keep the House value around, we could simply map again on the first line, like so:

    let details: [Observable<(House, Family)>] = house.map { house in 
      houseDetail(house.id).map { (house, $0) }
    }