RxSwiftで使えるオペレーターを備忘録的に書き綴っていきたいと思います。
また、公式Exanpleを参考にしています。
その1はこちら
その2はこちら
また、今回の内容はこちらの動画が非常に解りやすいです。
作業環境については、
を参考に構築して頂ければと思います。
早速、やっていきましょう!!
Combination Operators
複数のObservableを一つのObservableにまとめるオペレーターです。
startWith
func startWith() {
let disposeBag = DisposeBag()
Observable.of("🐶", "🐱", "🐭", "🐹")
.startWith("1️⃣")
.startWith("2️⃣")
.startWith("3️⃣", "🅰️", "🅱️")
.subscribe(onNext: { print($0) })
.disposed(by: disposeBag)
}
startWith() /*
3️⃣
🅰️
🅱️
2️⃣
1️⃣
🐶
🐱
🐭
🐹
*/
出力を見ると、startWithで指定した値から流れていますね。
更に、startWithが複数ある場合には、下から(記述が新しい順)から値が流れていきます。
merge
func merge() {
let disposeBag = DisposeBag()
let subject1 = PublishSubject<String>()
let subject2 = PublishSubject<String>()
Observable.of(subject1, subject2)
.merge()
.subscribe(onNext: { print($0) })
.disposed(by: disposeBag)
subject1.onNext("🅰️")
subject1.onNext("🅱️")
subject2.onNext("①")
subject2.onNext("②")
subject1.onNext("🆎")
subject2.onNext("③")
}
merge() /*
🅰️
🅱️
①
②
🆎
③
*/
merge()する事でサンプルコードでは2つのPublishSubject()が一つにまとめられてsubsctibeされていますね。
zip
func zip() {
let disposeBag = DisposeBag()
let stringSubject = PublishSubject<String>()
let intSubject = PublishSubject<Int>()
Observable.zip(stringSubject, intSubject) { stringElement, intElement in
"\(stringElement) \(intElement) がzipされました"
}
.subscribe(onNext: { print($0) })
.disposed(by: disposeBag)
stringSubject.onNext("🅰️")
stringSubject.onNext("🅱️")
intSubject.onNext(1)
intSubject.onNext(2)
stringSubject.onNext("🆎")
intSubject.onNext(3)
}
zip() /*
🅰️ 1 がzipされました
🅱️ 2 がzipされました
🆎 3 がzipされました
*/
zipはクロージャーで渡された値が結合されて流れてきます。(最大8つまで結合出来ます)
また、クロージャーで渡された値の1番目(stringElement)と1番目(intElement)、2番目(stringElement)と2番目(intElement)という結合になります。
対になる値が無いと出力されません。
combineLatest
func combineLatest() {
let disposeBag = DisposeBag()
let stringSubject = PublishSubject<String>()
let intSubject = PublishSubject<Int>()
Observable.combineLatest(stringSubject, intSubject) { stringElement, intElement in
"\(stringElement) \(intElement)"
}
.subscribe(onNext: { print($0) })
.disposed(by: disposeBag)
stringSubject.onNext("🅰️")
stringSubject.onNext("🅱️")
intSubject.onNext(1)
intSubject.onNext(2)
stringSubject.onNext("🆎")
}
combineLatest() /*
🅱️ 1
🅱️ 2
🆎 2
*/
combineLatestはコードの記述がzipと似ていますが、出力が全く違いますね!!
これは、クロージャーで渡された値の最新値をとっている為です。
stringElementとintElementが流れて、2つの要素が揃った瞬間にそれぞれの値の最新値が出力される訳です。
私自身初学者なりの体感としましてはcombineLatestは実際のApp製作でとりわけ使われている印象がありますので、しっかりインプットしたいと思います。(ログイン画面によく使われている印象があります)
また、配列としてクロージャーに渡す事も出来ます。
func arrayCombineLatest() {
let disposeBag = DisposeBag()
let stringObservable = Observable.just("❤️")
let fruitObservable = Observable.from(["🍎", "🍐", "🍊"])
let animalObservable = Observable.of("🐶", "🐱", "🐭", "🐹")
Observable.combineLatest([stringObservable, fruitObservable, animalObservable]) {
// 配列としてクロージャーに渡している
"\($0[0]) \($0[1]) \($0[2])"// 要素の1番目、2番目、3番目を取り出す
}
.subscribe(onNext: { print($0) })
.disposed(by: disposeBag)
}
arrayCombineLatest() /*
❤️ 🍎 🐶
❤️ 🍐 🐶
❤️ 🍐 🐱
❤️ 🍊 🐱
❤️ 🍊 🐭
❤️ 🍊 🐹
*/
出力される時間によって、最新値を出力されているので、🍐と🍊が切り替わるタイミングでのanimalObservableの出力を調整するのはとても難しそうです。😂
switchLatest
func switchLatest() {
let disposeBag = DisposeBag()
let subject1 = BehaviorSubject(value: "⚽️")
let subject2 = BehaviorSubject(value: "🍎")
let subjectsSubject = BehaviorSubject(value: subject1)
subjectsSubject.asObservable()
.switchLatest()
.subscribe(onNext: { print($0) })
.disposed(by: disposeBag)
// 初期値にsubject1を持っている為、subject1のイベントのみ流れる。(switchされている)
subject1.onNext("🏈")
subject1.onNext("🏀")
subjectsSubject.onNext(subject2)
// イベント発行がsubject2にswitchされた
subject1.onNext("⚾️")
subject2.onNext("🍐")
}
switchLatest() /*
⚽️
🏈
🏀
🍎
🍐
*/
switchLatestでは、subjectsSubjectが初期値としてsubject1を持っている為、順に⚽️(初期値)🏈,🏀,と流れ、次にonNext(subject2)が呼ばれた事により、流れるイベントが切り替わり、subject2の🍎(初期値)、🍐と流れます。
RxSwiftの処理はよく川に例えられますが、川の流れの様に処理を見ていくと、解りやすいかと思います。
withLatestFrom
func withLatestFrom() {
let disposeBag = DisposeBag()
let foodSubject = PublishSubject<String>()
let drinksSubject = PublishSubject<String>()
foodSubject.asObservable()
.withLatestFrom(drinksSubject) { "\($0) + \($1)" }
.subscribe(onNext: { print($0) })
.disposed(by: disposeBag)
foodSubject.onNext("🥗")
drinksSubject.onNext("☕️")
foodSubject.onNext("🥐")
drinksSubject.onNext("🍷")
foodSubject.onNext("🍔")
foodSubject.onNext("🍟")
drinksSubject.onNext("🍾")
}
withLatestFrom() /*
🥐 + ☕️
🍔 + 🍷
🍟 + 🍷
*/
withLatestFromはfoodSubjectに対して、withLatestFromしているので、foodSubjectにイベントが流れた際、withLatestFromの引数に指定したdrinksSubjectの最新値を出力しています。
以上になります!!
どんどん処理が複雑になってきましたが、サンプルコードを書き換えてビルドしていく事で処理の流れが理解しやすいかと思います。😄