I have two Publishers, I want to feed the second one with the result of first one, I could do what I wanted by calling the second one nested into the first one, it works but it does not feel good to look at, is there a better way to do it?
the first Publisher returns AnyPublisher<URL, Error>
and the second one returns AnyPublisher<JsonModel, Error>
func upload(input: URL, output: URL) {
let converter = MP3Converter()
converter.convert(input: input, output: output)
.sink { [weak self] result in
if case .failure(let error) = result {
ConsoleLogger.log(error)
self?.uploadedRecordingURL = nil
}
} receiveValue: { url in
guard let data = try? Data(contentsOf: url) else {
return
}
self.repository.uploadCover(data: data)
.sink(receiveCompletion: { [weak self] result in
if case .failure(let error) = result {
ConsoleLogger.log(error)
self?.uploadedRecordingURL = nil
}
}, receiveValue: { [weak self] response in
self?.uploadedRecordingURL = response.fileURL
}).store(in: &self.disposables)
}.store(in: &disposables)
}
CodePudding user response:
I think you've already deduced this, but just do be clear: you are using sink
and its receiveValue
completely wrong. Don't start a new chain or do any significant work here at all! There should be a simple sink at the end, followed by store to anchor the chain, and that's the end.
You are looking for flatMap
. That is how you chain publishers. (See my https://www.apeth.com/UnderstandingCombine/operators/operatorsTransformersBlockers/operatorsflatmap.html.) You may have to give some thought to exactly what needs to pass from the first publisher and its chain down into the flatMap closure and what needs to pass on down the chain from there.
CodePudding user response:
You could use flatmap to chain your publishers together.
// the code would roughly look like this
converter.convert(input: input, output: output).flatMap { data in
return self.repository.uploadCover(data: data)
}.sink(receiveCompletion: { [weak self] result in
if case .failure(let error) = result {
ConsoleLogger.log(error)
self?.uploadedRecordingURL = nil
}
}, receiveValue: { [weak self] response in
self?.uploadedRecordingURL = response.fileURL
}).store(in: &self.disposables)
Here's an example in playgrounds that compiles
import Combine
import Foundation
enum SomeError: Error {
}
let subject0 = CurrentValueSubject<Data, SomeError>(Data())
let pub0 = subject0.eraseToAnyPublisher()
func repoUpload(data: Data) -> AnyPublisher<URL, SomeError> {
// do the real work, this is just to get it to compile
let subject1 = CurrentValueSubject<URL, SomeError>(URL(fileURLWithPath: "Somepath"))
return subject1.eraseToAnyPublisher()
}
var disposables = Set<AnyCancellable>()
pub0.flatMap { data in
return repoUpload(data: data)
}.sink(receiveCompletion: { result in
print(result)
}, receiveValue: { response in
print(response)
}).store(in: &disposables)