Search code examples
swiftrx-swift

Order network requests result with RxSwift


I currently have a set of network requests to fire. The problem is that I need to order the results of them to fit the order I fired them.

My current code is the following:

for url in config.fieldImages.map ({ URL(string: $0)! }) {
    self.getWheelFieldImage(url: url)
        .takeUntil(.inclusive, predicate: { (_) -> Bool in
            images.count == config.fieldImages.count - 1
        })
        .subscribe(onNext: { (anImage) in
            images.append(anImage)
        }, onError: { (error) in
            completion(nil, nil, error)
        }, onCompleted: {
            completion(images, false, nil)
            self.lastUpdate = Date()
        }, onDisposed: {
    })
    .disposed(by: self.disposeBag)
}

I'm wondering if there is an easy way to order these results in the same order I fired them, using RxSwift.

EDIT:

I try to explain the problem better. I have this array with N URLs and I fire the requests one after the other (1,2,3,4...).

I need to have back the result from these requests in the same order (R1, R2, R3, R4, where R1 is the response from request 1 etc...) to store the images in the resulting array.

I can wait all to finish. No problem.


Solution

  • Without much changes in your original code you can achieve this by use using enumerated() on your urls list as:

    /// Prefill your images with nil for each image
    var images = Array<Int?>(repeating: nil, count: config.fieldImages.count)
    for (index, url) in config.fieldImages.map ({ URL(string: $0)! }).enumerated() {
        self.getWheelFieldImage(url: url)
            .takeUntil(.inclusive, predicate: { (_) -> Bool in
                images.count == config.fieldImages.count - 1
            })
            .subscribe(onNext: { (anImage) in
                images[index] = anImage /// Store on proper position
            }, onError: { (error) in
                completion(nil, nil, error)
            }, onCompleted: {
                completion(images, false, nil)
                self.lastUpdate = Date()
            }, onDisposed: {
        })
        .disposed(by: self.disposeBag)
    }
    

    Probably most RxWay will be use of zip operator as:

    let streams = config.fieldImages
        .map { URL(string: $0)! }
        .map { self.getWheelFieldImage(url: $0) }
    let images = Observable.zip(streams) // Observable<[UIImage]>
        .subscribe(
            onNext: { [weak self] images in
                completion(images, false, nil)
                self?.lastUpdate = Date()
            }, 
            onError: { error in
                completion(nil, nil, error)
            }
        )
        .disposed(by: self.disposeBag)
    

    You can read more about zip in documentation