자주 사용되는 Operator인데 생각해보니 차이점을 명확하게 이해한 후 사용하지 않고 그때그때 찾아서 사용했었어서 이번 기회에 명확히 개념을 정리하고자 한다.
Merge
여러 Observable이 방출하는 항목들을 하나의 Observable이 방출하도록 병합한다.
마블 다이어그램과 같이 첫번째 스트림과 두번째 스트림을 합쳐 하나의 스트림으로 만들어준다.
이때 첫번째 스트림, 두번째 스트림에 이벤트가 발생할 때 마다 바로바로 이벤트를 방출한다.
단순히 두 스트림의 이벤트를 하나로 합하기 때문에 스트림을 구별 할 수는 없다.
let firstSubject = BehaviorSubject(value: 1)
let secondSubject = BehaviorSubject(value: 2)
let thirdSubject = BehaviorSubject(value: 3)
Observable.of(firstSubject, secondSubject) // of를 통해 Observable 생성
.merge()
.subscribe { print($0) }
.disposed(by: bag)
firstSubject.onNext(4)
secondSubject.onNext(5)
첫 번째와 두 번째 subject를 묶어 merge후 값을 찍어보면,
가장 처음 subscribe를 한 시점에 firstSubejct의 값인 1, secondSubject의 값인 2가 차례로 찍힌다.
이후 onNext를 통해 이벤트를 전달하면 4와 5가 나타난다.
// 결과
next(1)
next(2)
next(4)
next(5)
let firstSubject = BehaviorSubject(value: 1)
let secondSubject = BehaviorSubject(value: 2)
let thirdSubject = BehaviorSubject(value: 3)
Observable.of(firstSubject, secondSubject) // of를 통해 Observable 생성
.merge()
.subscribe { print($0) }
.disposed(by: bag)
firstSubject.onNext(4)
secondSubject.onNext(5)
//추가
firstSubject.onCompleted()
firstSubject.onNext(6)
secondSubject.onNext(7)
추가로 firstSubject를 complete한 후 firstSubject에 이벤트를 전달하면 firstSubject는 6의 값을 방출하지 않지만,
complete되지 않은 secondSubject는 그대로 값 7을 방출한다.
CombineLatest
첫 번째 스트림과 두 번째 스트림을 결합한 후 계산 후 새로운 Observable을 만든다
merge와 다르게 각 스트림의 값을 그대로 전달하는게 아니라 새로운 값을 만든다. 이때 각 스트림의 값에 따라 다른 결과를 도출할 수 있다.
첫 번째 스트림에서 1이 방출되었지만 새로운 Observable에는 아무 값도 방출되지 않는다.
그러나 두 번째 스트림까지 값(A)이 방출되면 그 때부터 각 스트림에서 가장 마지막으로 방출된 값을 합해 새로운 값을 방출한다.
Observable.combineLatest(message, name) { msg, name -> String in
return "\(msg) \(name)"
}
.subscribe { print($0) }
.disposed(by: bag)
message.onNext("내 이름은 ")
name.onNext("릴리")
위 설명처럼 message.onNext()만 실행된 시점에는 아무런 값도 출력되지 않는다.
그러나 name.onNext()까지 실행된 시점에는
// 결과
next(내 이름은 릴리)
결과 값이 나타난다.
Observable.combineLatest(message, name) { msg, name -> String in
return "\(msg) \(name)"
}
.subscribe { print($0) }
.disposed(by: bag)
message.onNext("내 이름은 ") // 아무 값도 안 나옴
name.onNext("릴리") // print: 내 이름은 릴리
message.onNext("내 별명은 ") // print: 내 별명은 릴리
name.onNext("안나") // print: 내 별명은 안나
message.onCompleted()
message.onNext("나는") // 아무 값도 안 나옴
name.onNext("별이") // print: 내 별명은 별이
name.onCompleted() // completed 출력
위처럼 한 단계마다 주석과 같은 값이 나타난다.
message가 onComplete된 시점에 message에 이벤트를 전달해도 전달이 안되는건 merge와 동일하지만
message만 onComplete이고 name은 아직 onComplete되지 않은 시점에 name에 이벤트를 전달하면 message의 가장 마지막 값인 "내 별명은 "으로 값이 나타난다.
message와 name 모두 complete된 시점에 완전히 complete된다.
다만 둘 중 하나라도 error가 나오면 그 아래부터는 이벤트 전달이 안된다.
Zip
combineLastest와 비슷한데 다르다. 1과 A까지 방출했을 때는 combineLatest와 동일한데 Zip은 짝을 맞추어 방출한다.
즉 각 스트림에서 값을 방출하였을 때 동일한 인덱스에 해당하는 값을 묶어 값을 방출한다.
짝을 지어야 값이 나가는 식으로 생각하면 좋을 것 같다. 방출되는 시점은 짝이 이루어졌을 때!
위 그림에서 5는 짝이 없기 때문에 구독자에게 전달되지 않는다.
let key = PublishSubject<Int>()
let value = PublishSubject<String>()
Observable.zip(key, value) { "\($0) : \($1)"}
.subscribe { print($0) }
.disposed(by: bag)
key.onNext(1)
value.onNext("하나")
key에 1을 전달한 시점에는 아무 값도 구독자에게 전달되지 않는다.
value에 "one"이 전달된 시점에 구독자에 값이 전달된다.
// 결과
next(1 : 하나)
결과 값이 나타난다.
Observable.zip(key, value) { "\($0) : \($1) "}
.subscribe { print($0) }
.disposed(by: bag)
key.onNext(1) // 아무 값도 안 나옴
value.onNext("하나") // print: (1:하나)
value.onNext("둘") // 아무 값도 안 나옴
key.onNext(2) // print: (2:둘)
key.onNext(3) // 아무 값도 안 나옴
key.onNext(4) // 아무 값도 안 나옴
key.onCompleted()
value.onNext("셋") // print: (3:셋)
value.onNext("넷") // print: (4:넷)
value.onNext("다섯") // 아무 값도 안 나옴
value.onCompleted()
위 결과처럼 짝을 맞추어 결과값이 나타난다.
key가 onCompleted 되었더라도 그 전에 값을 방출하였는데 그 값이 짝을 못 이뤘다면 onCompleted 되었더라도 뒤늦게 방출된 value에 값과 짝을 이룰 수 있다.
key와 value 모두 complete된 시점에 완전히 complete된다.
combineLatest와 마찬가지로 둘 중 하나라도 error가 나오면 그 아래부터는 이벤트 전달이 안된다.
전에는 마냥 사용하기 바빳는데 한번 정리하니까 정리된 느낌이다. ㅠㅠ 대충은 알고 있긴 했는데 명확히 개념은 못잡았었는데 이번에 정리하니 차이가 명확하다.
가장 많이 사용할 것 같다고 느낀게 combineLatest이고 (가장 유연한 느낌?)
zip은 인덱스 다룰 때 사용한 적이 있었는데 그런 식으로 사용하면 좋겠다!
merge는 사용해 본 경험이 없는데 은근 많이 사용할 수 있을 것 같다.
머리에 박아놓고 가끔 읽어봐야겠다 ㅠㅠ..
'iOS' 카테고리의 다른 글
[iOS] Coordinator Pattern (1) | 2024.02.14 |
---|---|
[iOS/RxSwift] subscribe, bind, drive 비교하기 (0) | 2024.01.21 |
[iOS] NIB View 생성자로 인스턴스 생성하기 (BaseView, 클린코드 독후감..) (1) | 2023.11.27 |
[iOS] CollectionView Pagination과 Kingfisher로 이미지 캐싱(Kakao Search Image API) (0) | 2023.10.24 |
[iOS] Keychain(키체인) 알아보기 (0) | 2023.07.31 |