2

I have a subject which sends/receives an array of Data like PassthroughSubject<[Int], Never>(). When a value is received I want to split the array in single values to manipulate them and afterwards collect them again.

I know that the issue is that the flatMap never sends the completion Event. But how can I solves this issue? Or is there a better way to manipulate every value in an array with combine?

Edit: I don‘t want to complete the subject to collect. I want to collect the output of the sequencer.

Example:

import Combine

var storage = Set<AnyCancellable>()
let subject = PassthroughSubject<[Int], Never>()

subject
    .flatMap { $0.publisher }
    .map { $0 * 10 }
    .collect()
    .sink {
        print($0) // Never called
    }
    .store(in: &storage)

subject.send([1, 2, 3, 4, 5])
Noroxs
  • 115
  • 10

3 Answers3

1

You don't need the flatMap() and the collect() calls, you can simply map() over the received array:

subject
    .map { $0.map { $0 * 10 } }
    .sink {
        print($0) // Now it's called :)
    }
    .store(in: &storage)

subject.send([1, 2, 3, 4, 5])
Cristik
  • 30,989
  • 25
  • 91
  • 127
1

I found a solution how I achieved the my expected outcome. I had to move the map and collect within the flatMap.

import Combine

var storage = Set<AnyCancellable>()
let subject = PassthroughSubject<[Int], Never>()

subject
    .flatMap { $0.publisher
        .map { $0 * 10 }
        .collect()
    }
    .sink {
        print($0)
    }
    .store(in: &storage)

subject.send([1, 2, 3, 4, 5])
subject.send([1, 2, 3, 4, 5].reversed())

This will print [10, 20, 30, 40, 50] and [50, 40, 30, 20, 10].

Noroxs
  • 115
  • 10
  • See also https://stackoverflow.com/questions/61841254/combine-framework-how-to-process-each-element-of-array-asynchronously-before-pr for the case where the processing you are doing in `.map` is itself asynchronous. – matt Nov 12 '20 at 18:08
0

collect waits for the publisher to complete. A PassthroughSubject doesn't automatically complete. You need to call send(completion: on it.

subject.send([1, 2, 3, 4, 5])
subject.send(completion: .finished) // now `sink` will be triggered
Dávid Pásztor
  • 51,403
  • 9
  • 85
  • 116
  • But what can I do if I don‘t want to complete the subject? – Noroxs Nov 12 '20 at 16:47
  • @Noroxs don't use `collect`. The whole point of `collect` is to wait for the publisher to complete and only then emit the values, since it collects _all_ values and it can only know that _all_ values were emitted once the publisher completes. You could also use `collect(5)` to emit every time 5 values were published. – Dávid Pásztor Nov 12 '20 at 17:02
  • Careful if you send complete: .finished the sink will also no longer get values – Luke Jul 27 '22 at 15:21