在RxSwift中安排订阅

时间:2019-01-09 23:22:30

标签: ios swift xcode rx-swift

我正在尝试安排一系列图像下载。我想多次调用scheduleImagesDownload,但是只有在上一次调用完成后,才能在downloadImages中执行代码。

我正在尝试从相机中获取特定ID的图像,并下载了该ID的所有图像后,便想开始下载下一个ID的图像,依此类推。

我很难过,因为即使使用串行调度程序,在上一次下载完成之前,所有订阅都将立即被调用。我想知道是否有一种方法可以使用纯Rx,而不必使用信号量等。

提前谢谢!


    func scheduleImagesDownload(flightId: String) -> Disposable {
        let subscription = donwloadImages(flightId)
            .subscribeOn(SerialDispatchQueueScheduler(qos: .background))
            .subscribe(onCompleted: {
                /// Finished downloading images for flight.
            }, onError: { error in
                /// Error downloading images for flight.
            })

        return subscription
    }

    func donwloadImages(_ flightId: String) -> Completable {
        return Completable.create { completable in

            /// Simulate querying the drone for images to download async and start downloading them.
            DispatchQueue.global().async {
                sleep(5) // Sleep to simulate downloading time.
                completable(.completed) // Finished downloading.
            }
            return Disposables.create()
        }
    }

2 个答案:

答案 0 :(得分:1)

用于链接队列中每个图像下载操作的关键操作符是ConcatMap。 我已经根据您的要求编写了以下代码段。代码段几乎可以自我解释。

let flightIds: [String] = [] // Array holding flightIds
let disposeBag = DisposeBag()

func download() {
    Observable.from(flightIds) // Convert array of flightIds into Observable chain
        .flatMap(getImageURLs) // For each flightId, get array of image URLs to be downloaded
        .flatMap(convertToImageURLObservable) // Convert array of image URLs into Observable chain
        .concatMap(downloadImage) // Concate each url in observable chain so each image will be downloaded sequencially
        .subscribeOn(SerialDispatchQueueScheduler(qos: .background)) // Scheduled entire chain on background queue
        .subscribe()
        .disposed(by: disposeBag)
}

/// Fetches image URLs for given flightId
func getImageURLs(_ flightId: String) -> Single<[URL]> {
    return Single<[URL]>.create { single in

        /// fetch & pass URLs in below array inside .success
        single(.success([]))

        return Disposables.create()
    }
}

/// Convert array of image URLs into Observable chain
func convertToImageURLObservable(_ urls: [URL]) -> Observable<URL> {
    return Observable.from(urls)
}

/// Downloads image for given URL
func downloadImage(_ url: URL) -> Completable {
    return Completable.create { completable in

        /// fetch image
        DispatchQueue.global().async {
            sleep(5) // Sleep to simulate downloading time.
            completable(.completed) // Finished downloading.
        }

        return Disposables.create()
    }
}

答案 1 :(得分:0)

我相信这就是您想要的。基本上,您会创建一个从摄像机检索到的所有ID的数组/集合。然后在每次完成操作时,删除集合中的一个元素,然后重新启动该过程,直到集合为空。

    var cameraIds: Set<String>()

    func scheduleImagesDownload(flightId: String) -> Disposable {
        let subscription = donwloadImages(flightId)
            .subscribeOn(SerialDispatchQueueScheduler(qos: .background))
            .subscribe(onCompleted: {
                if let nextFlightId = cameraIds.first {
                  cameraIds.removeFirst()
                  scheduleImagesDownload(flightId: nextFlightId)
               } else {
                 // Finished downloads for all cameraIds and images
               }
            }, onError: { error in
                /// Error downloading images for flight.
            })

        return subscription
    }