Reputation: 109
I am trying to merge multiple publishers that are of different types.
I have publishers of type string and a publisher of type, however when I merge them using MergeMany
or CombineLatest
I get a type mismatch error.
Is there anyway to merge publishers of different types? See the code example below:
@Published var str1: String?
@Published var str2: String?
@Published var image: Image?
Publishers.MergeMany($str1, $str2, $image)
.removeDuplicates()
.sink { _ in
//...
}
.store(in: &bag)
Upvotes: 5
Views: 7005
Reputation: 1995
@jnpdx's answer gets the job done. I'm focusing on this question from OP:
Is there anyway to merge publishers of different types?
It is important to realize the difference between Merge and CombineLatest operators since they are quite different, semantically.
Check out RxMarbles for Merge and CombineLatest.
Merge requires the inner publishers to have the same Output
and emits a single value whenever one emits.
CombineLatest publishes a tuple containing the latest values for each inner publisher.
I would write this like so:
@Published var str1: String?
@Published var str2: String?
@Published var image: Image?
func setUpBindings() {
Publishers.CombineLatest3($str1, $str2, $image)
.sink { _ in
//...
}
.store(in: &bag)
}
Since tuples can't conform to Equatable (yet), you can't leverage removeDuplicates()
on the resulting publisher.
Like @jnpdx wrote, an approach could be to pull the .removeDuplicates()
inside to each publisher.
Publishers.CombineLatest3(
$str1.removeDuplicates(),
$str2.removeDuplicates(),
$image.removeDuplicates()
)
.sink { (str1, str2, image) in
Upvotes: 3
Reputation: 52575
Assuming that you had no other @Published
properties, the easiest thing to do would be to use objectWillChange
:
self.objectWillChange
.receive(on: RunLoop.main)
.sink {
print("objectWillChange...", self.str1, self.str2, self.image)
}
.store(in: &bag)
However, this does not give you the remove duplicates that you mentioned in the comments.
If you want, that, you can get a little more complicated with things:
Publishers.MergeMany(
$str1.dropFirst().removeDuplicates().map({ _ in }).eraseToAnyPublisher(),
$str2.dropFirst().removeDuplicates().map({ _ in }).eraseToAnyPublisher(),
$image.dropFirst().removeDuplicates().map({ _ in }).eraseToAnyPublisher()
)
.receive(on: RunLoop.main)
.sink {
print("MergeMany...", self.str1, self.str2, self.image)
}
.store(in: &bag)
(There may be a prettier way than my map. { _ in }
to remove the return type)
The above method seems to work effectively for removing the individual duplicate elements and only triggering the sink
when one of them has changed.
Upvotes: 5