Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 | 29 | 30 | 31 |
Tags
- Scenedelegate
- 이터레이터패턴
- 전략패턴
- Lifecycle
- 팩토리메서드패턴
- 데코레이터패턴
- 프록시패턴
- 상태패턴
- unowned
- 커맨드패턴
- Mobile
- ios
- 스트래터지패턴
- SWIFT
- RxSwift
- 템플릿메서드
- ViewController
- cocoapods
- 컴파운드패턴
- DispatchQueue
- 파사드패턴
- Xcode
- 어댑터패턴
- WKWebView
- 컴포지트패턴
- 추상팩토리패턴
- 싱글턴패턴
- 옵저버패턴
- 디자인패턴
- 스테이트패턴
Archives
- Today
- Total
ios dev kangwook.
7. Combining Operators 본문
Prefixing and concatenating
- Observable로 작업할 때 가장 먼저 필요한 것은 observer가 초기 값을 받도록 보장하는 것
- "현재 상태"에 대한 정보가 필요한 상황이 있다. → 현재위치 또는 네트워크 연결상태
example(of: "startWith") {
let numbers = Observable.of(2, 3, 4) // numbers 시퀀스를 생성
let observable = numbers.startWith(1) // 시퀀스의 시작을 1로 설정하고 numbers가 연결
_ = observable.subscribe(onNext: { value in
print(value)
})
}
- 결과적으로 startWith(_: )는 보다 일반적인 concat 연산자 계열의 간단한 변형
- 초기값은 RxSwift가 startWith(_ : ) 연결되는 시퀀스를 추가하는 한 아이템의 시퀀스
- Observable.concat(_ : ) 정적함수는 두 시퀀스를 연결
example(of: "Observable.concat") {
let first = Observable.of(1, 2, 3)
let second = Observable.of(4, 5, 6)
let observable = Observable.concat([first, second])
observable.subscribe(onNext: { value in
print(value)
})
}
- 시퀀스를 추가하는 또 다른 방법은 concat(_: ) 연산자
example(of: "concat") {
let germanCities = Observable.of("Berlin", "Münich", "Frankfurt")
let spanishCities = Observable.of("Madrid", "Barcelona", "Valencia")
let observable = germanCities.concat(spanishCities)
_ = observable.subscribe(onNext: { value in
print(value)
})
}
- concatMap(_: )은 클로저에 의해 생성된 각 시퀀스가 다음 시퀀스가 구독되기 전에 완료될 때까지 실행되도록 보장
- 따라서 concatMap(_: )은 순차 순서를 보장하는 편리한 방법
example(of: "concatMap") {
let sequences = [
"German cities": Observable.of("Berlin", "Münich", "Frankfurt"),
"Spanish cities": Observable.of("Madrid", "Barcelona", "Valencia")
]
let observable = Observable.of("German cities", "Spanish cities")
.concatMap { country in sequences[country] ?? .empty() }
_ = observable.subscribe(onNext: { string in
print(string)
})
}
Merging
- RxSwift는 시퀀스를 결합하는 여러 가지 방법을 제공
- 가장 쉬운 방법이 merge
example(of: "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", "Frankfurt"]
var rightValues = ["Madrid", "Barcelona", "Valencia"]
repeat {
switch Bool.random() {
case true where !leftValues.isEmpty:
left.onNext("Left: " + leftValues.removeFirst())
case false where !rightValues.isEmpty:
right.onNext("Right: " + rightValues.removeFirst())
default:
break
}
} while !leftValues.isEmpty || !rightValues.isEmpty
left.onCompleted()
right.onCompleted()
}
- merge() Observable은 수신하는 각 시퀀스를 구독하고 도착하자마자 아이템을 방출
- 사전 정의된 순서는 없다.
- merge() 는 source 시퀀스가 완료되고 모든 내부 시퀀스가 완료된 후에 완료
- 내부 시퀀스가 완료되는 순서는 관련이 없다.
- 시퀀스에서 오류가 발생하면 merge() Observable이 즉시 오류를 전달한 다음 종료
Combining Elements
- RxSwift의 필수 연산자 그룹은 combineLatest 와 관련된 연산자
- combineLatest는 여러 시퀀스의 값을 결합
- 결합된 내부 시퀀스 중 하나가 값을 내보낼 때마다 사용자가 제공하는 클로저를 호출
- 각 내부 시퀀스에서 보낸 마지막 값을 받는다.
- 예를 들어, 여러 텍스트필드를 한번에 관찰하고 해당 값을 결합하고 여러 소스의 상태를 확인하는 등 많은 구체적인 응용 프로그램이 있다.
example(of: "combineLatest") {
let left = PublishSubject<String>()
let right = PublishSubject<String>()
let observable = Observable.combineLatest(left, right) { lastLeft, lastRight in
"\(lastLeft) \(lastRight)"
}
let disposable = observable.subscribe(onNext: { value in
print(value)
})
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,")
left.onCompleted()
right.onCompleted()
}
- 각 시퀀스의 최신 값을 인수로 받는 클로저를 사용하여 Observable을 결합
- 결합된 각 관측 값이 하나의 값을 방출할 때까지 아무일도 일어나지 않는다. 그 후, 새로운 값을 방출할 때마다 클로저는 각 Observable의 최신 값을 수신하고 해당 아이템을 생성
- 일반적인 패턴은 값을 튜플에 결합한 다음 체인 아래로 전달하는 것
- 일반적으로 아래와 같이 값을 결합한 다음 filter(_: )를 호출하는 경우가 많다.
let observable = Observable
.combineLatest(left, right) { ($0, $1) }
.filter { !$0.0.isEmpty }
- combineLatest 계열의 연산자에는 여러 가지 변형이 있다.
- 2개에서 8개 사이의 Observable 시퀀스를 매개 변수로 사용
example(of: "combine user choice and value") {
let choice: Observable<DateFormatter.Style> = Observable.of(.short, .long)
let dates = Observable.of(Date())
let observable = Observable.combineLatest(choice, dates) { formate, when -> String in
let formatter = DateFormatter()
formatter.dateStyle = format
return formatter.string(from: when)
}
_ = observable.subscribe(onNext: { value in
print(value)
})
}
- 이 예제는 사용자 설정이 변경될 때마다 화면의 값이 자동 업데이트
- 또 다른 Combination Operator에는 zip 연산자 계열이 있다.
- combineLatest와 마찬가지로 여러 가지 변형이 있다.
example(of: "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)
})
}
- zip의 이점
- 제공한 Observable을 구독
- 각각이 새로운 값을 방출할 때까지 기다린다.
- 두가지 새로운 값으로 클로저를 호출
- zip은 동일한 논리적 위치(1st와 1st, 2nd와 2nd 등)에서 각 Observable의 값을 쌍으로 만든다.
- 이는 논리적 위치가 맞지 않는 아이템이 있을 경우 zip은 더 이상 아무것도 보내지 않는다. → 따라서 위의 예에서 "Vienna"가 출력되지 않는다.
- 이를 indexed sequencing이라고 한다.
- 그러나 zip은 초기에 값 방출을 중지할 수 있지만 모든 내부 Observable이 완료될 때까지 자체적으로 완료되지 않아 각 항목이 작업을 완료할 수 있다.
- Swift에도 zip 연산자가 있지만 두 컬렉션의 항목에 대해서만 새로운 튜플 컬렉션을 만든다.
- RxSwift는 2~8개의 Observable에 대한 변형을 제공
Triggers
- 앱에는 다양한 요구 사항이 있으며 여러 입력 소스를 관리해야 한다.
- 한 번에 여러 Observable의 입력을 받아야 하는 경우가 많다.
- 일부는 단순히 코드에서 작업을 트리거하고 다른 일부는 데이터를 제공
- withLatestFrom(_: )
example(of: "withLatestFrom") {
let button = PublishSubject<Void>()
let textField = PublishSubject<String>()
let observable = button.withLatestFrom(textField) // 버튼이 값을 내보내면 무시하지만 대신 텍스트필드에서 받은 최신 값을 내보낸다.
_ = observable.subscribe(onNext: { value in
print(value)
})
textField.onNext("Par")
textField.onNext("Pari")
textField.onNext("Paris")
button.onNext(())
button.onNext(())
}
- sample(_ : ) → withLatestFrom(_: ) 과 유사
- 다른 Observable에서 최신 값을 방출하지만 마지막 "tick" 이후에 도착한 경우에만 방출
example(of: "sample") {
let button = PublishSubject<Void>()
let textField = PublishSubject<String>()
let observable = textField.sample(button) // textField 이벤트를 받다가 button 이벤트가 발생했을 때 값 방출
_ = observable.subscribe(onNext: { value in
print(value)
})
textField.onNext("Par")
textField.onNext("Pari")
textField.onNext("Paris")
button.onNext(())
button.onNext(())
}
- withLatestFrom(_: ) 은 Observable 데이터를 매개 변수로 사용하고 sample(_: )은 Observable 트리거를 매개 변수로 사용
- 트리거를 기다리는 것은 UI 작업을 할 때 큰 도움
Switches
- RxSwift에는 두 가지 주요 Switching Operator인 amb(_: ) 및 switchLatest() 가 있다.
- 둘 다 결합된 또는 source 시퀀스의 이벤트 간에 전환하여 Observable 시퀀스를 생성할 수 있다.
- 이를 통해 구독자가 런타임에 수신할 시퀀스의 이벤트를 결정할 수 있다.
- amb(_: ) → amb를 ambiguous로 생각
example(of: "amb") {
let left = PublishSubject<String>()
let right = PublishSubject<String>()
let observable = left.amb(right)
let _ = observable.subscribe(onNext: { value in
print(value)
})
left.onNext("Lisbon")
right.onNext("Copenhagen")
left.onNext("London")
left.onNext("Madrid")
right.onNext("Vienna")
left.onCompleted()
right.onCompleted()
}
- amb(_: ) 연산자는 left 및 right Observable 항목을 구독
- 그둘 중 하나가 아이템을 방출할 때까지 기다린 다음 다른 아이템에서 구독을 취소
- 그 후에는 첫 번째 활성 Observable의 아이템만 전달
- 중복 서버에 연결하고 먼저 응답하는 서버를 고수하는 것과 같은 몇 가지 실용적인 응용 프로그램을 선택할 수 있다.
- switchLatest() → 더 많은 곳에서 사용되는 옵션
example(of: "switchLatest") {
let one = PublishSubject<String>()
let two = PublishSubject<String>()
let three = PublishSubject<String>()
let source = PublishSubject<Observable<String>>()
let observable = source.switchLatest()
let disposable = observable.subscribe(onNext: { value in
print(value)
})
source.onNext(one) // one을 선택하면서 one에 대한 이벤트를 출력
one.onNext("Some text from sequence one")
two.onNext("Some text from sequence two")
source.onNext(two) // two를 선택하면서 two에 대한 이벤트를 출력
two.onNext("More text from sequence two")
one.onNext("and also from sequence one")
source.onNext(three) // three를 선택하면서 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을 선택하면서 one에 대한 이벤트를 출력
one.onNext("Nope. It's me, one!")
disposable.dispose()
}
- 기존에 배운 flatMapLatest와 거의 똑같은 일을 한다.
- flatMapLatest는 최신 값을 Observable에 매핑한 다음 구독
- switchLatest와 마찬가지로 최신 구독만 활성 상태를 유지
Combining elements within a sequence
- reduce(_ : _ : ) → Swift 표준 라이브러리에도 구현되어 있다.
- Swift의 컬렉션과 비슷하지만 Observable 시퀀스를 사용
example(of: "reduce") {
let source = Observable.of(1, 3, 5, 7, 9)
let observable = source.reduce(0, accumulator: +)
_ = observable.subscribe(onNext: { value in
print(value)
})
// 동일한 코드
print("===============================")
let observable = source.reduce(0) { summary, newValue in
return summary + newValue
}
_ = observable.subscribe(onNext: { value in
print(value)
})
}
- 제공한 초기값(여기서는 0)으로 시작
- Observable이 항목을 내보낼 때마다 reduce(_: _: )는 클로저를 호출하여 새로운 summary를 생성
- source Observable이 완료되면 reduce(_: _: )가 summary 값을 방출하고 완료
Note
reduce(_: _: )는 source Observable이 완료될 때만 누적 값을 생성.완료되지 않은 시퀀스에 이 연산자를 적용하면 아무것도 내보내지 않는다.
- scan(_: _: ) → reduce(_: _: )와 비슷하게 동작하나 값을 바로 방출
example(of: "scan") {
let source = Observable.of(1, 3, 5, 7, 9)
let observable = source.scan(0, accumulator: +)
_ = observable.subscribe(onNext: { value in
print(value)
})
}
- source Observable이 아이템을 방출할 때마다 scan(_: accumulator: )은 클로저를 호출
- 새 아이템과 함께 실행 값을 전달하고 클로저는 새로운 누적 값을 반환
- scan 사용사례는 상당히 크다.
- scan Observable 상태 정보를 캡슐화하는 것은 좋은 아이디어. → 로컬 변수를 사용할 필요가 없으며 source Observable이 완료되면 사라진다.
'RxSwift' 카테고리의 다른 글
9. Beginning RxCocoa (0) | 2022.12.29 |
---|---|
8. Time-Based Operators (0) | 2022.12.27 |
6. Transforming Operators (0) | 2022.12.22 |
5. Filtering Operators (0) | 2022.12.19 |
4. Observables & Subjects in Practice (1) | 2022.12.13 |
Comments