How to flat map publisher of publishers in Combine

Issue #451

For some services, we need to deal with separated APIs for getting ids and getting detail based on id.

To chain requests, we can use flatMap and Sequence, then collect to wait and get all elements in a single publish

FlatMap

Transforms all elements from an upstream publisher into a new or existing publisher.

1
struct FlatMap<NewPublisher, Upstream> where NewPublisher : Publisher, Upstream : Publisher, NewPublisher.Failure == Upstream.Failure

Publishers.Sequence

A publisher that publishes a given sequence of elements.

1
struct Sequence<Elements, Failure> where Elements : Sequence, Failure : Error
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
func fetchItems(completion: @escaping ([ItemProtocol]) -> Void) {
requestCancellable = URLSession.shared
.dataTaskPublisher(for: topStoriesUrl())
.map({ $0.data })
.decode(type: [Int].self, decoder: JSONDecoder())
.flatMap({ (ids: [Int]) -> AnyPublisher<[HackerNews.Item], Error> in
let publishers = ids.prefix(10).map({ id in
return URLSession.shared
.dataTaskPublisher(for: self.storyUrl(id: id))
.map({ $0.data })
.decode(type: HackerNews.Item.self, decoder: JSONDecoder())
.eraseToAnyPublisher()
})

return Publishers.Sequence<[AnyPublisher<HackerNews.Item, Error>], Error>(sequence: publishers)
// Publishers.Sequence<[AnyPublisher<HackerNews.Item, Error>], Error>
.flatMap({ $0 })
// Publishers.FlatMap<AnyPublisher<HackerNews.Item, Error>, Publishers.Sequence<[AnyPublisher<HackerNews.Item, Error>], Error>>
.collect()
// Publishers.Collect<Publishers.FlatMap<AnyPublisher<HackerNews.Item, Error>, Publishers.Sequence<[AnyPublisher<HackerNews.Item, Error>], Error>>>
.eraseToAnyPublisher()
// AnyPublisher<[HackerNews.Item], Error>
})
.receive(on: RunLoop.main)
.eraseToAnyPublisher()
.sink(receiveCompletion: { completionStatus in
switch completionStatus {
case .finished:
break
case .failure(let error):
print(error)
}
}, receiveValue: { items in
completion(items)
})
}

Comments