Reputation: 8388
I have to make several api calls (approx 100) using a for loop and on completion of this I need to complete the Observable. I am using it as following:
func getMaterialInfo(materialNo:[String]) -> Observable<[String: Material]>{
return Observable.create({ (observable) -> Disposable in
for (index,mat) in materialNo.enumerated(){
// Pass the material number one by one to get the Material object
self.getMaterialInfo(materialNo: mat).subscribe(onNext: { material in
var materialDict: [String: Material] = [:]
materialDict[material.materialNumber] = material
observable.onNext(materialDict)
if index == (materialNo.count-1){
observable.onCompleted()
}
}, onError: { (error) in
observable.onError(error)
}, onCompleted: {
}).disposed(by: self.disposeBag)
}
return Disposables.create()
})
}
Although loop is working fine and observable.onCompleted() is called but the caller method does not receive it. I am calling it like following:
private func getImage(materialNo:[String]){
if materialNo.isEmpty {
return
}
var dictMaterials = [String:String]()
materialService.getMaterialInfo(materialNo: materialNo).subscribe(onNext: { (materials) in
for (key,value) in materials{
if (value.imageUrl != nil){
dictMaterials[key] = value.imageUrl
}
}
}, onError: { (error) in
}, onCompleted: {
self.view?.updateToolImage(toolImageList: dictMaterials)
}, onDisposed: {}).disposed(by: disposeBag)
}
OnCompleted block of Rx is not executing. How can I fix it?
Upvotes: 5
Views: 4046
Reputation: 1995
I revisited this answer, because I'm not sure what my brain was doing when I wrote the code sample below. I'd do something like this instead:
func getMaterialInfo(materialNo: String) -> Observable<[String: Material]> {
// ...
}
func getMaterialInfo(materialNumbers:[String]) -> Observable<[String: Material]>{
let allObservables = materialNumbers
.map { getMaterialInfo(materialNo: $0) }
return Observable.merge(allObservables)
}
From your code, I interpret that all individual getMaterialInfo
calls are done concurrently. Based on that, I would rewrite your getMaterialInfo(:[_])
method to use the .merge
operator.
func getMaterialInfo(materialNo:[String]) -> Observable<[String: Material]>{
return Observable.create({ (observable) -> Disposable in
// a collection of observables that we haven't yet subscribed to
let allObservables = materialNo
.map { getMaterialInfo(materialNo: $0) }
return Observable.merge(allObservables)
}
return Disposables.create()
}
Note that using merge
subscribes to all observable simultaneously, triggering 100 network requests at the same time. For sequential subscription, use concat
instead!
Upvotes: 1