apple/RxSwift, ReactorKit

[week6] Combining Observables

lgvv 2021. 7. 11. 20:29

✅ 이번 시간에는 Combining Observables에 대해서 알아보자

 

실습 코드는 

https://github.com/lgvv/MyRxSwift

 

lgvv/MyRxSwift

나의 RxSwift 공부 기록장. Contribute to lgvv/MyRxSwift development by creating an account on GitHub.

github.com

 

(목차)
1. Concatenation

 - startwith

 - concat

 - concatMap

2. Combining Elements

 - Combine Latest

 - zip

3. Trigger

 - withLatestFrom

4. Switches

 - ambiguous

 - switchLatest

5. Combining Elements within a Sequence

 - reduce

 - scan

6. 공식문서를 기반으로 Combining Observables

 - join : 공식문서에는 있지만 rxswift에서는 사용 안됨.

 - And/Then/When 

✅ startwith

 

startwith

        print(" ===== stratwith =====" )
        Observable.of(2,3,4)
            .startWith(1)
            .subscribe(onNext : { print($0) })

// 결과값
1
2
3
4

코드를 보면 아주 쉽게 이해할 수 있다.

 

✅ concat

 - startWith는 concat에서 파생되어 나온 것.

 - 두 sequence를 이어주는 역할을 해요

❗️다 스트림의 데이터 타입이 같아야 한다

concat에 대한 설명

 

        print(" ===== concat =====" )
        let first = Observable.of(1,2,3)
        let second = Observable.of(4,5,6)
        
        Observable
            .concat([first,second])
            .subscribe(onNext : { print($0) })
            .disposed(by: disposeBag)
 
// 결과값
1
2
3
4
5
6

concat은 다른 언어에서도 보인 형태로 쉽게 이해할 수 있었다

 

✅ concatMap

 - concatMap은 flatMap과 비슷하다.

하나의 subscribe가 완료되어야 다음 subscribe로 넘어간다.

        print(" ===== concatMap =====" )
        let sequences = [
            "Germany" : Observable.of("Berlin","Munich","FrankFrut"),
            "Spain" : Observable.of("Madrid","Barcelona","Valencia")
        ]
        
        Observable.of("Germany","Spain")
            .concatMap { country in
                sequences[country] ?? .empty()
            }
            .subscribe(onNext : { string in
                print(string)
                
            })
            
// 결과값
Berlin
Munich
FrankFrut
Madrid
Barcelona
Valencia

코드를 보면 쉽게 이해할 수 있다!!

 

 

✅ Merging

 - 순서대로 합쳐준다.

 - 여기서 주의할 점은 , error처리가 되는 부분만 신경쓰자.

merge에 대한 설명과 error가 처리되는 법

        print(" ===== merge ===== ")
        let left = PublishSubject<String>()
        let right = PublishSubject<String>()
        
        let source = Observable.of(left.asObservable(),right.asObservable())
        let observable = source.merge()
        let disposable = observable.subscribe(onNext : {value in
            print(value)
        })
        
        var leftValues = ["Berlin","Munich","FrankFrut"]
        var rightValues = ["Madrid","Barcelona","Valencia"]
        
        repeat{
            if arc4random_uniform(2) == 0{
                if !leftValues.isEmpty {
                    left.onNext("Left : " + leftValues.removeFirst())
                }
            }
            else if !rightValues.isEmpty {
                right.onNext("Right : " + rightValues.removeFirst())
            }
        }while !leftValues.isEmpty || !rightValues.isEmpty
        
        disposable.dispose()
        
// 결과값
Left : Berlin
Right : Madrid
Left : Munich
Left : FrankFrut
Right : Barcelona
Right : Valencia

코드를 한번 볼까? 

이번에 조금 특이한 asObservable이라는 것이 나오는데 지금은 사용되지 않는 Varible 즉 변수의 옵저버블을 읽기위해서 사용되는 메소드이다 저걸 하용하면 읽어 들일 수 있다.

 

✅ CombineLatest

 - 두 항목을 합치는데, 다른 한쪽의 값을 가장 최근의 값으로 사용하여 합친다.

 - 타입이 달라도 적용할 수 있다.

CombineLatest

        print(" ===== combineLatest ===== ")
        Observable
            .combineLatest(left,right) { lastLeft, lastRight in
                "\(lastLeft)\(lastRight)"
            }
            .subscribe(onNext: { print($0) })
            .disposed(by: disposeBag)
        
        print("> Sending a value to Left")
        left.onNext("Hello,")
        
        print("> Sending a value to Right")
        right.onNext("world")
        
        print("> Sending another value to Right")
        right.onNext("RxSwift")
        
        print("> Sending another value to Left")
        left.onNext("Have a good day")


// 결과값
> Sending a value to Left
Hello,
> Sending a value to Right
world
Hello,world


        print(" ===== combineLatest(다른타입) ===== ")
        let choice : Observable<DateFormatter.Style> = Observable.of(.short,.long)
        
        let dates = Observable.of(Date())
        
        let observable = Observable.combineLatest(choice,dates) { (format,when) -> String in
            let formatter = DateFormatter()
            formatter.dateStyle = format
            return formatter.string(from: when)
        }
        
        observable.subscribe(onNext : { value in
            print(value)
        })



// 결과값
7/11/21
July 11, 2021
> Sending another value to Right
RxSwift
Hello,RxSwift
> Sending another value to Left
Have a good day
Have a good dayRxSwift

코드를 보면 쉽게 이해할 수 있다. 클로저 부분으로 작성한 건데 저 부분은 resultSelector: 를 작성하면 클로저로 바뀐다.

 

✅ zip

 - combinLatest와 비슷하나 얘는 1:1 매칭이 되어야지만 방출된다.

zip

        print(" ===== zip ===== ")
        
        let zipleft : Observable<Weather> = Observable.of(.sunny,.cloudy,.cloudy,.sunny)
        let zipright = Observable.of("Lisbon","Copenhagen","London","Madrid","Vienna")
        Observable
            .zip(zipleft,zipright) {  weather, city in
                
                return "It's \(weather) in \(city)"
            }
            .subscribe(onNext : { print($0) })
            .disposed(by: disposeBag)
            


// zip
It's sunny in Lisbon
It's cloudy in Copenhagen
It's cloudy in London
It's sunny in Madrid

zip도 코드를 보면 쉽게 이해할 수 있다!

 

✅ withLatestFrom

 - 버튼을 두번 탭해서 가장 최신 상태를 유지시켜주는 것을 의미한다.

withLatestFrom

// 선언부  
enum Weather {
    case cloudy
    case sunny
}
    
// 구현부
        print(" ===== withLatestFrom ===== ")
        let button = PublishSubject<Void>()
        let textField = PublishSubject<String>()
        
        button
            .withLatestFrom(textField)
            .subscribe(onNext : {value in
                print(value)
            })
        
        textField.onNext("Par")
        textField.onNext("Pari")
        textField.onNext("Paris")
        button.onNext(())
        button.onNext(())
        
// 결과값
Paris
Paris

코드를 보면 쉽게 이해할 수 있어. 최신 결과값으로 갱신 받는다고 했지?

버튼을 탭했을 때, 우리는 결과값으로 가장 최신의 결과를 받는걸 확인할 수 있었다!!

 

✅ ambiguous

 - ambiguous 에서는 여러 개를 ambiguous하게 subscribe 하고 있다가 가장 먼저 방출되는 원소를 확인하고 그 subject만 subscribe합니다

ambiguous

        print(" ===== ambiguous ===== ")
        let ambleft = PublishSubject<String>()
        let ambright = PublishSubject<String>()
        ambleft.amb(ambright)
            .subscribe(onNext : { print($0) })
            .disposed(by: disposeBag)
        
        ambleft.onNext("Lisbon")
        ambright.onNext("Copenhagen")
        ambleft.onNext("London")
        ambleft.onNext("Madrid")
        ambright.onNext("Vienna")
    
// 결과값
Lisbon
London
Madrid

코드를 보면 이해가 쉽지!! 가장 먼저 방출하는거 서브젝트만 내보내!

 

switchLatest

 - 이건 스위치랑 사용법이 비슷한데, 가장 최근에 방출된 원소를 switch해준다.

switchLatest를 공식문서에서 검색하면 이리로 간다.

        print(" ===== switchLatest ===== ")
        let one = PublishSubject<String>()
        let two = PublishSubject<String>()
        let three = PublishSubject<String>()
        
        let source = PublishSubject<Observable<String>>()
        
        source
            .switchLatest()
            .subscribe(onNext : { print($0) })
            .disposed(by: disposeBag)
        
        source.onNext(one)
        one.onNext("Some text from sequence one")
        two.onNext("Some text from sequence two")
        
        source.onNext(two)
        two.onNext("More text from sequence two")
        one.onNext("and also from sequence one")
        
        source.onNext(three)
        two.onNext("Why don't you see me?")
        one.onNext("I'm alone, help me")
        three.onNext("Hey it's three. I win.")
        
        source.onNext(one)
        one.onNext("Nope it's me, one!")
        

// 결과값
Some text from sequence one
More text from sequence two
Hey it's three. I win.
Nope it's me, one!

publishSubject를 사용하면 어차피 이전 값에 대한 정보가 저장되지 않아 자연스럽게 최신 정보를 반영하지만 다른 서브젝트들은 이전 값도 갖고 있어서 이걸 사용하면 가장 최근값 1개만 사용할 수 있다.

 

reduce

 - reduce는 sequence를 진행하면서 어떤 수행을 해주며 마지막 값만 방출한다.

reduce

 

        print(" ===== reduce ===== ")
        Observable.of(1,2,3,4,5)
            .reduce(0, accumulator: +)
            .subscribe(onNext : { print($0) })
            .disposed(by: disposeBag)
        
        print(" ===== reduce ex2 ===== ")
        Observable.of(1,2,3,4,5)
            .reduce(0, accumulator: { sum, value in
                return sum + value
            })
            .subscribe(onNext : { print($0) })
            .disposed(by: disposeBag)
            
 //결과값
  ===== reduce ===== 
15
 ===== reduce ex2 ===== 
15

둘이 같으나 클로저를 통해 다른 작업할 수도 있다

둘이 같으나 클로저를 통해 다른 작업 할 수 있어! so easy!

 

✅ scan

 - 스캔은 다른 시간에도 보았지만, 결과값 하나하나를 보여준다!

scan

        print(" ===== scan ===== ")
        Observable.of(1,2,3,4,5)
            .scan(0, accumulator: +)
            .subscribe(onNext : { print($0) })
            .disposed(by: disposeBag)
            
// 결과값
1
3
6
10
15

스캔에 대해서 자세하게 알고 싶다면 내 포스팅에서 지난주차를 참고하기 바란다.

 

우선 여기서 마치도록 하자

'apple > RxSwift, ReactorKit' 카테고리의 다른 글

🐉 RxSwift(Relay와 subject)  (0) 2021.07.12
[week7] ⏰ Time Based Operators(cold? hot?)  (0) 2021.07.12
[week5] 🌟Transforming Observables  (0) 2021.07.10
[week4] Filtering Observables  (0) 2021.07.10
[week3] Subjects  (0) 2021.07.10