Let’s get straight to the point. What’re the differences?
combineLatest
It combines multiple observable sources and when each of them emits an item, it will fetch the other sources’ latest items and merge them into a single observable.
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
let first = PublishSubject<Int>()
let second = PublishSubject<String>()
let disposeBag = DisposeBag()
Observable
.combineLatest(first, second) { ($0, $1) }
.subscribe(onNext: { print($0) })
.disposed(by: disposeBag)
first.onNext(1)
second.onNext("A")
first.onNext(2)
second.onNext("B")
second.onNext("C")
second.onNext("D")
first.onNext(3)
first.onNext(4)
/*
print out
(1, "A")
(2, "A")
(2, "B")
(2, "C")
(2, "D")
(3, "D")
(4, "D")
*/
Notice that at the beginning, when the first emitted “1”, the combineLatest
operator didn’t do anything. Because at this point the second source didn’t produce any item. Until the second source produced the first string “A” then the combineLatest
started working. From then on, whenever each of them emitted a fresh item, the operator combined it with the other one’s latest item and passed it to the subscriber.
You can only put up to 8 sources into combineLatest
. But…of course, you hack it by combining other combineLatest
observers to surpass the number limit.
withLatestFrom
withLatestFrom
might sound similar to combineLatest
but actually, their mechanisms are quite different. Let’s take a look at an example:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
let first = PublishSubject<String>()
let second = PublishSubject<String>()
let disposeBag = DisposeBag()
first
.withLatestFrom(second) { $0 + $1 }
.subscribe(onNext: { print($0) })
.disposed(by: disposeBag)
first.onNext("1")
second.onNext("A")
first.onNext("2")
second.onNext("B")
second.onNext("C")
second.onNext("D")
first.onNext("3")
first.onNext("4")
/* print out
2A
3D
4D
*/
The rule is: when the first source emits an item, it will fetch the second source’s latest item and pass them to the subscriber. You can only put one parameter in withLatestFrom
. And just like combineLatest
, withLatestFrom
only works when both sources have at least one value. On top of that, notice when the second emits “B” and “C”, the print function doesn’t fire. The reason is that we observe the change of the first source. The first source is the main character and only when it passes a new item then will we check the second source’s value. If the first source doesn’t emit a new one, the observer doesn’t care about the second source’s changes.
zip
zip
is similar to combineLatest
in many ways: they both combine up to 8 sources and they are triggered when each of the sources emits a new item. The main difference is zip
fetches items in the order. Here is an usage example:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
let first = PublishSubject<String>()
let second = PublishSubject<String>()
let disposeBag = DisposeBag()
Observable
.zip(first, second) { $0 + $1 }
.subscribe(onNext: { print($0) })
.disposed(by: disposeBag)
first.onNext("1")
second.onNext("A")
first.onNext("2")
second.onNext("B")
second.onNext("C")
second.onNext("D")
first.onNext("3")
first.onNext("4")
/* print out
1A
2B
3C
4D
*/
You can think of it as using an index to fetch elements from two different arrays. The sources’ elements pair according to the same index.
merge
merge
observes multiple sources at the same time and when each of them emits an element, you get that single item right away. Things to note about the merge
is that the observables’ arguments must be the same type.
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
Observable
.of(first, second)
.merge()
.subscribe(onNext: { print($0) })
.disposed(by: disposeBag)
first.onNext("1")
second.onNext("A")
first.onNext("2")
second.onNext("B")
second.onNext("C")
second.onNext("D")
first.onNext("3")
first.onNext("4")
/* print out
1
A
2
B
C
D
3
4
*/
Comments powered by Disqus.