While working on an app where I needed to subscribe to multiple Combine publishers, I got confused about if I should use merge, zip or combineLatest. These publishers are quite similar with subtle differences. For making sure I never get confused about it, I am going to present examples in this week’s blog post.
Merge
Merge publisher just re-publishes any values received from any of the publisher. Useful when there are multiple sources of data we would like to combine into a single flow of updates.
| @Published var state1 = "0" | |
| @Published var state2 = "a" | |
| func mergeExample() { | |
| $state1.merge(with: $state2) | |
| .sink { value in | |
| print("sink", value) | |
| } | |
| .store(in: &cancellables) | |
| print("will change state1 to 1") | |
| state1 = "1" | |
| print("will change state1 to 2") | |
| state1 = "2" | |
| print("will change state2 to b") | |
| state2 = "b" | |
| print("will change state1 to 3") | |
| state1 = "3" | |
| print("will change state2 to c") | |
| state2 = "c" | |
| } | |
| /* output: | |
| sink: 0 | |
| sink: a | |
| will change state1 to 1 | |
| sink: 1 | |
| will change state1 to | |
| sink: 2 | |
| will change state2 to b | |
| sink: b | |
| will change state1 to 3 | |
| sink: 3 | |
| will change state2 to c | |
| sink: c | |
| */ |
Zip
Zip waits until it has received at least one element from each of the underlying publisher, and then delivers the value as a tuple. If one of the publisher publishes multiple values, then the first received value is part of the tuple and other values are part of next tuples after that.
| @Published var state1 = "0" | |
| @Published var state2 = "a" | |
| func zipExample() { | |
| $state1.zip($state2) | |
| .sink { value in | |
| print("zip", value) | |
| } | |
| .store(in: &cancellables) | |
| print("will change state1 to 1") | |
| state1 = "1" | |
| print("will change state1 to 2") | |
| state1 = "2" | |
| print("will change state2 to b") | |
| state2 = "b" | |
| print("will change state1 to 3") | |
| state1 = "3" | |
| print("will change state2 to c") | |
| state2 = "c" | |
| } | |
| /* output | |
| sink ("0", "a") | |
| will change state1 to 1 | |
| will change state1 to 2 | |
| will change state2 to b | |
| sink ("1", "b") | |
| will change state1 to 3 | |
| will change state2 to c | |
| sink ("2", "c") | |
| */ |
CombineLatest
CombineLatest publishes a tuple whenever any of the underlying publishers emits an element. The tuple contains the latest value from each of the publisher.
| @Published var state1 = "0" | |
| @Published var state2 = "a" | |
| func combineLatestExample() { | |
| $state1.combineLatest($state2) | |
| .sink { value in | |
| print("sink", value) | |
| } | |
| .store(in: &cancellables) | |
| print("will change state1 to 1") | |
| state1 = "1" | |
| print("will change state1 to 2") | |
| state1 = "2" | |
| print("will change state2 to b") | |
| state2 = "b" | |
| print("will change state1 to 3") | |
| state1 = "3" | |
| print("will change state2 to c") | |
| state2 = "c" | |
| } | |
| /* example | |
| sink ("0", "a") | |
| will change state1 to 1 | |
| sink ("1", "a") | |
| will change state1 to 2 | |
| sink ("2", "a") | |
| will change state2 to b | |
| sink ("2", "b") | |
| will change state1 to 3 | |
| sink ("3", "b") | |
| will change state2 to c | |
| sink ("3", "c") | |
| */ |
If this was helpful, please let me know on Mastodon@toomasvahter or Twitter @toomasvahter. Feel free to subscribe to RSS feed. Thank you for reading.