I have used two functions. First combineLatest
:
enum Weather {
case cloudy
case sunny
}
let left: Observable<Weather> = Observable.of(.sunny, .cloudy, .cloudy, .sunny)
let right = Observable.of("Lisbon", "Copenhagen", "London", "Madrid", "Vienna")
let observable = Observable.combineLatest(left, right) { weather, city in
return "It's \(weather) in \(city)"
}
_ = observable.subscribe(onNext: { value in
print(value)
})
with output:
It's sunny in Lisbon It's cloudy in Lisbon It's cloudy in Copenhagen It's cloudy in Copenhagen It's cloudy in London It's sunny in London It's sunny in Madrid It's sunny in Vienna
and zip
:
enum Weather {
case cloudy
case sunny
}
let left: Observable<Weather> = Observable.of(.sunny, .cloudy, .cloudy, .sunny)
let right = Observable.of("Lisbon", "Copenhagen", "London", "Madrid", "Vienna")
let observable = Observable.zip(left, right) { weather, city in
return "It's \(weather) in \(city)"
}
_ = observable.subscribe(onNext: { value in
print(value)
})
with output:
It's sunny in Lisbon It's cloudy in Copenhagen It's cloudy in London It's sunny in Madrid
How does it work? I understand that zip just map values one to one, and if there is no value for same index, it just skip it. But how to understand combineLatest
?
combineLatest
is a bit easier to understand (and more useful!) if the two observables don't emit elements immediately. An easier-to-understand example (which I encourage you to try) is:
Observable.combineLatest(someUISwitch.rx.isOn, someUITextField.rx.text) { isOn, text in
return "Text: \(text ?? ""), Switch is on: \(isOn)"
}.subscribe(onNext: { value in
print(value)
}).disposed(by: disposeBag)
Try adding a UISwitch
and a UITextField
into your UI. Now try entering some text into the text field, and also try switching the switch. You will see that whenever one of the two changes, that is, when either observable emits a new value, their combined value (combined by the closure passed to combineLatest
) gets emitted by the combined observable.
More generally, the observable produced by combineLatest
emits its first value when all the observables first emits a value, and subsequent values are emitted when any of the observables emits a value. Whenever it emits a value, the value it emits is computed by combining all the latest values that all the observables have emitted. Hence "combine latest".
For your Observable.of
example, the values are all emitted immediately, but combineLatest
behaves as if left
and right
emits values in an interlaced order, and left
goes first. I've tried to add annotations to the output you got. See if this helps your understanding.
(left emits sunny)
(right emits Lisbon)
It's sunny in Lisbon (the first values emitted by both observables)
(left emits cloudy)
It's cloudy in Lisbon
(right emits Copenhagen)
It's cloudy in Copenhagen
(left emits 2nd cloudy)
It's cloudy in Copenhagen
(right emits London)
It's cloudy in London
(left emits sunny)
It's sunny in London
(right emits Madrid)
It's sunny in Madrid
(right emits Vienna, since left ran out)
It's sunny in Vienna
Also, don't forget there's RxMarbles that you can play with.