[RxSwift] 2. Observable?
2. Observable?
What is Observable?
observable = observable sequence = sequence
- Observable은 시간의 흐름에 따라, 비동기적(asynchronous)으로 event를 생성한다 이러한 과정을 emit이라 부른다
- Observable을 Observer가 구독하고 Observable이 어떤 상태를 방출하였을 때 Observer에게 알림을 보낸다
Lifecycle of Observable
public enum Event<Element> {
/// Next elemet is produced.
case next(Element)
/// Sequence terminated with an error.
case error(Swift.Error)
/// Sequence completed successfully.
case completed
}
next
하나의 sequence를 나타내는 그림이다
화살표는 sequence의 lifetime(시간의 흐름)을 나타내고
1, 2, 3은 sequence의 lifetime동안 발생한 next event를 의미한다
completed
sequence의 lifetime동안 3개의 tap이벤트(next event)가 발생했고
오른쪽의 “ | ” 모양이 있는데 이것이 completed event에 해당한다 |
completed가 발생하면 sequence가 종료되고, 더이상 어떤 이벤트도 발생하지 않는다
error
X표시가 sequence에서 error event가 발생했다는 뜻이다.
error event도 completed event와 마찬가지로 발생하면 sequence가 종료되고,
해당 sequence에서는 더이상의 이벤트가 발생하지 않는다
Create an Observable
Observable.just()
오직 하나의 element를 포함하는 Observable Sequence를 생성
let observable: Observable<Int> = Observable<Int>.just(1)
let observable: Observable<String> = Observable<String>.just("1")
Observable.of()
가변적인 element를 포함하는 Observable Sequence를 생성
let observable = Observable<Int>.of(1,2,3,4,5)
- element가 많지만 배열이 아닌 Int타입이다
- 가변적으로 element들을 넣을 수 있다
- 인자로 Array를 넣게되면 단일 요소를 가지게된다 (
just
와 동일한 결과)
let observable = Observable<[Int]>.of([1,2,3])
// [1, 2, 3]
Observable.from()
오직 Array타입만 처리하며 각각 요소들을 하나씩 emit하는 기능
let observable = Observable.from([1, 2, 3])
/*
1
2
3
*/
Observable.range()
start부터 count크기 만큼의 값을 갖는 Observable을 생성
let observable = Observable<Int>.range(start: 3, count: 6)
/*
3
4
5
6
7
8
*/
Observable.empty()
- 요소를 가지지 않는 Observable
.completed
이벤트만 방출한다
let observable = Observable<Void>.empty()
- Observable은 반드시 특정 타입으로 정의되어야 하는데 타입 추론을 할 수 없으므로
Void
는 적절한 타입.
Observable.creat()
- Observable에 직접 event를 방출한다
Observable<Int>.create({ (observer) -> Disposable in
observer.onNext(3)
observer.onNext(4)
observer.onNext(5)
observer.onCompleted()
return Disposables.create()
})
/*
3
4
5
*/
이외에도 .never
, .interval
, .repeatElemet
등이 있다
Subscribe an Observable
- Observable은 구독되기 전까진 아무런 이벤트로 방출하지 않는다
- 따라서 구독을 하기 전엔 위의 코드들은 실행되지 않는다
subscribe(onNext: , onCompleted:, onError: )
et observable = Observable<Int>.range(start: 3, count: 6)
observable.subscribe(onNext: { element in
print(element)
}, onCompleted: {
print("onCompleted")
}, onError: {
print("onError")
)).dispose()
/*
3
4
5
6
7
8
onCompleted
*/
Disposing
- subscribe가 Observable안에 있는 이벤트들을 방출하는 것이라면, disposing은 subscribe를 취소하는 것
dispose()
- 구독을 취소하여 Observable을 수동적으로 종료시킴
let observable = Observable.of("A", "B", "C")
let subscription = observable.subscribe({ (event) in
print(event)
})
subscription.dispose()
DisposeBag()
- dispose에 대한 리턴값을 담는 객체
- 각각의 구독에 대해 관리하는 것은 효율적이지 못하기 때문에 DisposeBag을 이용해 Disposable들을 모아놨다가 한번에 처분하는 방식
let disposeBag = DisposeBag()
Observable.of("A", "B", "C")
.subscribe{
print($0)
}
.disposed(by: disposeBag)
Traits
Trait은 Observable보다 좁은 범위의 Observable로 코드의 가독성을 높이기 위해 사용함
Single
- 항상 한가지 값 또는 오류 알림 둘중 하나만 방출함
.success(value)
or.error
.success(value)
는.next
+.completed
이벤트의 결합이라고 볼 수 있다- 파일 다운로드나 디스크로딩 같은 로직에 어울림
Completable
- Single과 기능이 유사하지만 값을 방출하지 않음
- 작업이 잘 수행 됐는지 검토할 때 사용(파일 쓰기)
.completable(.completed)
,.completable(.error(value))
Maybe
- Single과 Completable을 섞어 놓은 형태
.success(value)
,.completed
,.error
를 방출한다
private let disposeBag = DisposeBag()
enum FileReadError: Error {
case fileNotFound, unreadable, encodingFailed
}
func loadText(from name: String) -> Single<String> {
return Single.create{ single in
let disposable = Disposables.create()
guard let path = Bundle.main.path(forResource: name, ofType: "txt") else {
single(.error(FileReadError.fileNotFound))
return disposable
}
guard let data = FileManager.default.contents(atPath: path) else {
single(.error(FileReadError.unreadable))
return disposable
}
guard let contents = String(data: data, encoding: .utf8) else {
single(.error(FileReadError.encodingFailed))
return disposable
}
single(.success(contents))
return disposable
}
}
// test
loadText(from: "myFile")
.subscribe{
switch $0 {
case .success(let string):
print(string)
case .error(let error):
print(error)
}
}.disposed(by: disposeBag)
// prints: fileNotFound
}
Do & Debug
Observable.do()
.never
연산자여도 일단 실행한다.never
연산자는completed
도 출력하지 않는다Observable.never
는subscribe()
가 실행되지만completed
가 출력되지 않아subscribe()
이 동작했는지 알 수 없다do
를 사용하면subscribe()
가 발생된지 알 수 있다
example(of: "never") {
let observable = Observable<Any>.never()
let disposeBag = DisposeBag()
// 구독했음을 알리는 print("Subscribed")
observable.do(
onSubscribe: { print("Subscribed")}
).subscribe(
onNext: { (element) in
print(element)
},
onCompleted: {
print("Completed")
}
)
.disposed(by: disposeBag)
}
// prints: Subscribed
Observable.debug()
debug
연산자로 출력문을 통해 디버깅이 가능하다
example(of: "never") {
let observable = Observable<Any>.never()
let disposeBag = DisposeBag()
observable
.debug("never 확인")
.subscribe()
.disposed(by: disposeBag)
/* prints
2020-05-21 19:46:23.534: never 확인 -> subscribed
2020-05-21 19:46:23.536: never 확인 -> isDisposed
*/
}