Consider that you have following implementation:
Plot
extension EntryViewModel {
enum UserAction {
case one, two, three
}
}
class EntryViewModel {
private var userActionSubject: PassthroughSubject<UserAction, Never> = .init()
public var userActionPublisher: AnyPublisher<UserAction, Never> = .empty() // It doesn't matter, It will be set somewhere in setup.
init() {
}
func setup() {
self.userActionPublisher = self.userActionSubject.eraseToAnyPublisher()
}
}
And you have a list of these entries.
class ListViewModel {
@Published var entries: [EntryViewModel] = []
}
You would like to build a Handler
which will handle these UserAction
.
class Handler {
var subscription: AnyCancellable?
func configured(stream: AnyPublisher<UserAction, Never>) {
self.subscription = stream.sink{[weak self] self?.process($0)}
}
}
List view model could
call updates on this entries
list as remove
or add
via common =
private func update(entries: [EntryViewModel]) {
let difference = entries.difference(from: self.entries) { $0.id == $1.id }
if let result = self.entries.applying(difference) {
self.entries = result
}
else {
// We should set all entries, because our collection is empty?
self.entries = entries
}
}
The most difficult part is Merging all entries events into one
stream.
func stream(from viewModel: ListViewModel) -> AnyPublisher<UserAction, Never> {
viewModel.$entries.flatMap { entries in
Publishers.MergeMany({entries.map(\.userActionSubject)})
}.eraseToAnyPublisher()
}
Question
When I add or remove entry
from entries
in ListViewModel
by applying difference and call setter, I catch interesting behavior.
Entry send several updates to handler.
I assume that it is happening, because stream capture several copies
(middle copies ) of this entries
array.
At the start I have:
[A, B, C]
entries.
I merge them into:
[A, B, C].map({\.publisher}).merge
Next, updates do the following:
D
entry is coming.
[A, B, C, D]
array of entries is correctly computed and setter is ok.
But! On Publisher side it will be
[A, B, C].map({\.publisher}) + [A, B, C, D].map({\.publisher})
.