I have a pipeline in ReactiveSwift for uploads. I want to make sure that even if one of the uploads fail, the rest will not be interrupted.
After all of them complete with success or failure, I should either return success from the performUploads method, or if any have failed, I should return an error, so the next step, the downloads part won't start. Even if there are errors, all uploads should have a chance to upload, they should not be stopped.
Is there a way to figure out if there are any errors after the uploads complete? See the methods here:
let pendingUploadItemsArray: [Items] = ...
func performUploads() -> SignalProducer<(), MyError> {
return upload(pendingUploadItemsArray)
.then(doAnything())
}
private func upload(_ items: [Items]) -> SignalProducer<Signal<(), MyError>.Event, Never> {
let producers = items
.filter { item in
return item.readyForUpload
}
.map { self.upload($0).materialize() }
return SignalProducer.merge(producers)
}
private func upload(_ item: Item) -> SignalProducer<(), MyError> {
return internalUploader.upload(item)
.on(failed: failedToUpload(item),
value: successfullyUploaded(item))
.ignoreValues()
}
where the internalUploader upload method is:
func upload(_ item: Item) -> SignalProducer<Any, MyError>
And then in another class you would call this uploader:
let sync = self.uploader.performUploads()
.then(startDownloads())
The startDownloads should only run if all the uploads have completed with success.
Thanks for any insight.
This might be something that should be done in a completely different manner.
I'm don't know exactly what
successfullyUploadedandfailedToUploadare doing in your code, but presumably you're keeping track of successes and failures to provide some kind of live progress UI. This is how I would structure it:UploadResultstruct that represents an item that has succeeded or failed to upload.uploadfunction, instead of creating an array of producers and then merging them, I convert the array of items into a signal producer of items withSignalProducer(items)and then useflatMap(.merge)to merge the uploads into a single signal producer.materialize, I usemapto convert successful uploads into anUploadResultand I useflatMapErrorto convert failed uploads into anUploadResult.scanto accumulate the results as each upload completes. Each time an upload finishes (either successfully or with an error),scanwill send an updated array of upload results that can be used to update the UI.Then you could use it like this:
on(value:)operator to update the UI based on the current results. Each time a download succeeds or fails, this closure will be called with the updated results.take(last: 1)to filter out all intermediate results; it'll only send along the final results when all uploads have completed.attemptto check if any of the uploads have failed and throw an error if so. This ensures the downloads will only be started if all uploads succeeded.Hopefully this handles your use case, but let me know in a comment if I missed something about the broader context!
EDIT
If you only care about dealing with results one at a time rather than as a running array, you can get rid of the
scanand then replacetake(last: 1)withcollect: