RxSwift:使用运算符组合下载照片并在本地保存

时间:2016-10-29 19:28:15

标签: swift rx-swift reactivex

我试图通过被动方式执行某些操作:

  1. 索取照片下载
  2. next个活动
  3. 获取下载进度
  4. 完成后,然后在本地保存该照片
  5. 所以我开始使用RxSwift并像

    一样实现它
    photoController.downloadPhoto(photoItem.photo)
    .doOnNext { downloadTaskInfo in
        photoItem.viewState = .NetworkProgress(task: downloadTaskInfo.task, progress: downloadTaskInfo.progress)
    }
    .flatMapLatest { downloadTaskInfo in
        return PHPhotoLibrary.savePhoto(downloadTaskInfo.buffer)
    }
    .observeOn(MainScheduler.instance)
    .subscribe(
        onError: { error in
            photoItem.viewState = .NetworkFailed
        },
        onCompleted: {
            photoItem.viewState = .Default
        }
    )
    .addDisposableTo(disposeBag)
    

    但是flatMapLatest没有做我期待的事情。我认为flatMapLatest允许我抓住最新的事件并进行另一次操作。

    所以,我决定用reduce替换它以实现我的想法,但我认为它不是正确的操作符,因为我不想加入所有的下载进度一个变量。我想要的是可以等待下载完成然后获得最新的其他操作,例如在本地保存照片。 使用concat我无法从第一个Observable接收结果。

    我需要像

    这样的东西
    // 
    .waitUntilDownloadFinishesAndContinueWith { downloadTaskInfo in
        return PHPhotoLibrary.savePhoto(downloadTaskInfo.buffer)
    }
    

    有人能解释我正确的设计方法吗?

    更新

    我决定和withLatestFrom一起去,但即使如此,我也遇到了一些问题。 downloadPhotoObservable过早处置。

    let downloadPhotoObservable = photoController.downloadPhoto(photoItem.photo)
        .doOnNext { downloadTaskInfo in
            photoItem.viewState = .NetworkProgress(task: downloadTaskInfo.task, progress: downloadTaskInfo.progress)
        }
    
    Observable.just(photoItem)
        .withLatestFrom(downloadPhotoObservable)
        .map { downloadTaskInfo in
            PHPhotoLibrary.savePhoto(downloadTaskInfo.buffer)
        }
        .observeOn(MainScheduler.instance)
        .subscribe(
            onError: { error in
                photoItem.viewState = .NetworkFailed
            },
            onCompleted: {
                photoItem.viewState = .Default
            }
        )
        .addDisposableTo(disposeBag)
    

    我确实做错了。

1 个答案:

答案 0 :(得分:1)

所以,我找到了实现我想要做的事情的方法。我决定过滤所有结果并比较最终的buffer长度。 buffer是照片持久性的下一部分。

photoController.downloadPhoto(photoItem.photo)
    .downloadProgress()
    // Receive the download progress
    .doOnNext { downloadTaskInfo in
        photoItem.viewState = .NetworkProgress(task: downloadTaskInfo.task, progress: downloadTaskInfo.progress)
    }
    // Wait for the complete buffer
    .filter { downloadTaskInfo in
        downloadTaskInfo.contentLength == Int64(downloadTaskInfo.buffer.length)
    }
    // Save it locally
    .flatMap { downloadTaskInfo in
        PHPhotoLibrary.savePhoto(downloadTaskInfo.buffer)
    }
    .observeOn(MainScheduler.instance)
    .subscribe(
        onError: { error in
            photoItem.viewState = .NetworkFailed
        },
        onCompleted: {
            photoItem.viewState = .Default
        }
    )
    .addDisposableTo(disposeBag)

BTW,我正在使用scan运算符来回忆进度信息。我使用名为downloadProgress的自定义运算符创建了一个快捷方式:

extension ObservableType where E == NetworkDataTaskInfo {
    func downloadProgress() -> Observable<NetworkDownloadTaskInfo> {
        let seed = NetworkDownloadTaskInfo(task: NopNetworkTask(), buffer: NSMutableData(), progress: 0, contentLength: 0)
        return scan(seed, accumulator: { latestDownloadTaskInfo, currentDataTaskInfo in
            var downloadedProgress: Float = 0
            var contentLength: Int64 = 0

            if let response = currentDataTaskInfo.response {
                // Start
                contentLength = response.expectedContentLength
            }
            else if let data = currentDataTaskInfo.data {
                // Accumulate
                contentLength = latestDownloadTaskInfo.contentLength
                latestDownloadTaskInfo.buffer.appendData(data)
                downloadedProgress = Float(latestDownloadTaskInfo.buffer.length) / Float(contentLength)
            }

            if contentLength <= 0 {
                throw NSURLError.ZeroByteResource
            }

            // Accumulated info
            return NetworkDownloadTaskInfo(
                task: currentDataTaskInfo.task,
                buffer: latestDownloadTaskInfo.buffer,
                progress: downloadedProgress,
                contentLength: contentLength
            )
        })
    }
}