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
- 전략패턴
- 옵저버패턴
- 팩토리메서드패턴
- 컴포지트패턴
- 추상팩토리패턴
- ViewController
- SWIFT
- WKWebView
- 어댑터패턴
- 커맨드패턴
- DispatchQueue
- 상태패턴
- 프록시패턴
- 싱글턴패턴
- 템플릿메서드
- ios
- 컴파운드패턴
- 데코레이터패턴
- 파사드패턴
- unowned
- Xcode
- RxSwift
- cocoapods
- Scenedelegate
- 스트래터지패턴
- 디자인패턴
- 이터레이터패턴
- Lifecycle
- Mobile
- 스테이트패턴
Archives
- Today
- Total
ios dev kangwook.
2. RxSwift Observables 본문
What is an Observable?
- Observable은 Rx의 핵심
- Observable은 일정 기간 동안 이벤트를 생성하며 그 과정에서 방출을 한다.
- 이벤트는 숫자 또는 사용자 지정 타입의 인스턴스와 같은 값을 포함하거나 탭과 같은 인식된 제스쳐일 수 있다.
- 이를 개념화하는 가장 좋은 방법 중 하나는 타임 라인에 표시된 marble diagram
- 왼쪽에서 오른쪽 화살표는 시간을 나타내고 번호가 새겨진 원은 시퀀스 요소를 나타낸다.
- 1이 방출되고 시간이 지나면 2와 3이 차례로 방출
- Observable의 생명 주기동안 어느 시점에나 있을 수 있다.
Lifecycle of an Observable
- 끝에 수직으로 된 막대가 있다는 것은 끝을 나타낸다.
- 이 Observable은 세 개의 탭 이벤트를 내보낸 후 종료
- 이 이벤트는 종료되었기 때문에 완료된 이벤트라고 한다.
- 이 때, Observable은 더 이상 이벤트를 방출하지 않는다.D
- 이 다이어그램에서는 오류가 발생
- 빨간색 X로 표시
- 이 또한 Observable이 정상적으로 종료되었을 때와 마찬가지로 더 이상 다른 이벤트를 방출하지 않는다.
- Observable은 아이템을 포함하는 다음 이벤트를 내보낸다.
- 종료 이벤트가 발생할 때까지 이 작업을 계속할 수 있다.(onError or onCompleted)
- Observable이 종료되면 더 이상 이벤트를 생성할 수 없다.
// Event<Element>
public enum Event<Element> {
// Next element is produced.
case next(Element)
// Sequence terminated with an error.
case error(Swift.Error)
// Sequence completed successfully.
case completed
}
Creating Observables
// Create Observable
// SupportCode.swift
public func example(of description: String, action: () ->Void) {
print("\n--- Example of:", description, "---")
action()
}
// RxSwiftPlayground
example(of: "just, of, from") {
let one = 1
let two = 2
let three = 3
let observable = Observable<Int>.just(one)
let observable2 = Observable.of(one, two, three) // [Int] 타입이 아닌 Observable<Int> 타입
let observable3 = Observable.of([one, two, three]) // Observable<[Int]> 타입
let observable4 = Observable.from([one, two, three]) // Observable<Int> 타입
}
- 하나의 정수로 just 메소드를 사용하여 Int 타입의 Observable 시퀀스를 만든다.
- "just"는 단 하나의 아이템만 포함하는 Observable 시퀀스를 만드든 것으로 Observable 타입 메소드
- Rx에서는 메소드를 Operator라고 한다.
- "of" Operator는 가변 매개 변수를 취하고 Swift는 이를 기반으로 Observable의 타입을 추론할 수 있다.
- Observable 배열을 만들고 싶다면 배열을 of에 전달할 수 있다.
- "from" Operator는 타입이 지정된 아이템에서 개별 아이템의 Observable을 만든다.
- "from" Operator는 배열만 받는다.
Subscribing to Observable
- RxSwift는 Observable과는 다른 옵저버에게 알림을 브로드 캐스트
// UIKeyboardDidChangeFrame
let observer = NotificationCenter.default.addObserver(forName: UIResponder.keyboardDidChangeFrameNotification,
object: nil,
queue: nil) { notification in
// Handle receiving notification
}
- RxSwift Observable을 구독하는 것은 상당히 유사
- Observable을 observing 한다고 한다.
- addObserver() 대신 subscribe() 를 사용
- Observable은 실제로 시퀀스 정의
- Observable을 구독하는 것은 Swift 표준 라이브러리의 Iterator에서 next()를 호출하는 것과 매우 유사
// Iterator next()
let sequence = 0..<3
var iterator = sequence.makeIterator()
while let n = iterator.next() {
print(n)
}
//0
//1
//2
- 하지만 Observable subscribe()은 이보다 더 간단
// Observable subscribe()
example(of: "subscribe") {
let one = 1
let two = 2
let three = 3
let observable = Observable.of(one, two, three)
observable.subscribe { event in // Observable 구독
print(event)
}
}
- Observable은 각 아이템에 대해 .next 이벤트를 내보낸 후 .completed 이벤트를 내보내고 종료
// Observable subscribe() - 2
observable.subscribe { event in
if let element = event.element {
print(element)
}
}
- event에는 element 속성이 있다.
- .next 이벤트에만 element가 있으므로 옵셔널 값
- 따라서 옵셔널 바인딩을 사용하여 nil이 아닌 경우 element 래핑을 해제
- 이것은 좋은 패턴이고 자주 사용되어 RxSwift에 적용되어 있다.
// Observable subscribe() - 3
observable.subscribe(onNext: { element in
print(element)
})
- 값 범위에서 Observable을 생성하는 것도 가능
- 시작 정수 값 및 생성할 정수의 개수를 사용하는 범위 연산자를 사용하여 Observable을 생성
- 방출된 각 원소에 대해 n번째 피보나치 수를 계산하고 출력
// Observable range
example(of: "range") {
let observable = Observable<Int>.range(start: 1, count: 10)
observable.subscribe(onNext: { i in
let n = Double(i)
let fibonacci = Int(((pow(1.61803, n) - pow(0.61803, n)) / 2.23606).rounded())
print(fibonacci)
})
}
Disposing and Terminating
- Observable은 구독을 받기 전까지는 아무것도 하지 않는다.
- .error 또는 .completed 이벤트를 생성하고 종료될 때까지 새로운 이벤트를 생성하는 Observable의 작업을 트리거 하는 구독
- 구독을 취소하여 Observable을 수동으로 종료할 수 있다.
// dispose
example(of: "dispose") {
let observable = Observable.of("A", "B", "C") // Observable<String> 생성
let subscription = observable.subscribe { event in // Observable 구독
print(event) // 출력
}
subscription.dispose() // 구독을 명시적으로 취소 -> Observable 이벤트 생성 중지
}
- 각 구독을 개별적으로 관리할 수 있지만 RxSwift에는 DisposeBag 타입이 포함
- DisposeBag이 할당 해제될 때 DisposeBag 안에 있는 각각에 대해 dispose()를 호출
// DisposeBag
example(of: "DisposeBag") {
let disposeBag = DisposeBag() // disposeBag 생성
Observable.of("A", "B", "C") // Observable 생성
.subscribe { // Observable 구독
print($0)
}.disposed(by: disposeBag) // subscribe의 반환 값을 disposeBag에 추가
}
- DisposeBag에 구독을 추가하는 것을 잊었거나 구독을 마쳤을 때 dispose를 수동으로 호출하거나 다른 방식으로 Observable이 어느 시점에서 종료된다고 하면 메모리 누수가 발생할 수 있다.
- Swift 컴파일러는 사용하지 않는 disposable에 대해 경고를 한다.
- create 연산자를 사용하는 것은 Observable이 subscriber에게 방출할 모든 이벤트를 지정하는 또 다른 방법
- create 연산자는 subscribe라는 단일 매개 변수를 사용
- subscriber에게 보낼 모든 이벤트를 정의
- subscribe 매개 변수는 AnyObserver를 취하고 Disposable을 리턴하는 이스케이프 클로저
- AnyObserver는 Observable 시퀀스에 값을 추가하는 것을 용이하게 하는 일반 타입으로 subscriber에게 방출
// create
example(of: "create") {
let disposeBag = DisposeBag()
Observable<String>.create { observer in
observer.onNext("1") // Observer에 .next 이벤트를 추가
observer.onCompleted() // Observer에 .completed 이벤트를 추가
observer.onNext("2") // Observer에 다른 .next 이벤트를 추가
return Disposable.create() // disposable 반환
}.subscribe(
onNext: { print($0) },
onError: { print($0) },
onCompleted: { print("Completed") },
onDisposed: { print("Disposed") }
).disposed(by: disposeBag)
}
두 번째 .next 이벤트는 .completed 이벤트를 생성하고 시퀀스에 추가되기 전에 종료되었기 때문에 출력되지 않는다.
Creating Observable Factories
- subscriber를 기다리는 Observable을 만드는 대신 각 subscriber에게 새로운 Observable을 제공하는 Observable 팩토리를 만드는 것이 가능
// deferred
example(of: "deferred") {
let disposeBag = DisposeBag()
var flip = false // create Bool flag
let factory: Observable<Int> = Observable.deferred { // deferred 연산자를 사용하여 Int 팩토리의 Observable을 생성
flip.toggle() // 팩토리를 subscribe 할 때마다 전환
if flip { // flip 이라는 flag에 따라 다른 Observable을 반환
return Observable.of(1, 2, 3)
} else {
return Observable.of(4, 5, 6)
}
}
for _ in 0...3 {
factory.subscribe(onNext: {
print($0, terminator: "")
})
.disposed(by: disposeBag)
print()
}
}
Using Traits
- Trait은 일반 Observable보다 행동 집합이 더 좁은 Observable
- Trait의 목적은 코드를 읽는 사람이나 API를 사용하는 사람에게 의도를 보다 명확하게 전달할 수 있는 방법을 제공하는 것
- RxSwift에는 Single, Maybe 및 Completable의 세 가지 Trait이 있다.
- Single은 .success(value) 또는 .error 이벤트를 내보낸다
- .success(value)는 실제 .next 및 .completed 이벤트의 조합
- 이는 데이터를 다운로드하거나 디스크에서 로드하는 것과 같이 성공하고 값을 산출하거나 실패하는 일회성 프로세스에 유용
- Completable은 .completed 또는 .error 이벤트만 내보낸다.
- 어떤 값도 방출하지 않는다.
- 파일 쓰기와 같이 작업이 성공적으로 완료되거나 실패한 경우에만 Completable을 사용할 수 있다.
- Maybe는 Single과 Completable의 mashup
- .success(value), .completed 또는 .error를 방출할 수 있다.
- 성공하거나 실패할 수 있는 작업을 구현하고 선택적으로 성공시 값을 반환해야 하는 경우 Maybe 사용
// Single
example(of: "Single") {
let disposeBag = DisposeBag()
enum FileReadError: Error {
case fileNotFound, unreadable, encodingFailed
}
func loadTxt(from name: String) -> Single<String> {
let disposable = Disposables.create()
guard let path = Bundle.main.path(forResource: name, ofType: "txt") else {
single(.failure(FileReadError.fileNotFound))
return disposable
}
guard let data = FileManager.default.contents(atPath: path) else {
single(.failure(FileReadError.unreadable))
return disposable
}
guard let contents = String(data: data, encoding: .utf8) else {
single(.failure(FileReadError.encodingFailed))
return disposable
}
single(.success(contents))
return disposable
}
loadTxt(from: "copyright")
.subscribe {
switch $0 {
case .success(let string)
print(string)
case .failure(let error)
print(error)
}
}.disposed(by: disposeBag)
}
'RxSwift' 카테고리의 다른 글
6. Transforming Operators (0) | 2022.12.22 |
---|---|
5. Filtering Operators (0) | 2022.12.19 |
4. Observables & Subjects in Practice (1) | 2022.12.13 |
3. RxSwift Subjects (0) | 2022.12.12 |
1. RxSwift Introduction (1) | 2022.12.07 |
Comments