Observable의 개념에 대해 알아 보겠습니다.
Observable이란
observable, observable sequence, sequence라는 표현을 쓰는데 사실 다 같은 말입니다.
중요한것은 이벤트가 비동기적으로 생성하는 기능 이라는것이고, 계속해서 이벤트를 생성하는데 이러한 과정을 Emit이라고 합니다.
또한 각각의 이벤트들은 숫자나 커스텀한 인스턴스등과 같은 값을 가질 수 있고, 또는 탭과 같은 제스처일 수도 있습니다.
1. Observable의 생명주기
세 개의 구성요소를 확인 할 수 있다
Observable에는 next, completed, error로 구성되어 있습니다.
- next 이벤트를 통해 각각의 요소들을 방출.
- 세 번의 tap 이벤트를 방출한 뒤 이벤트들이 다 방출되면 완전 종료.
- 즉 completed이벤트가 발생.
- 1, 2를 방출하고 에러가 발생해 error를 통해 종료.
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 이벤트는 어떠한 Element 인스턴스를 가지고 있는 것을 확인할 수 있다.
- .error 이벤트는 Swift.Error 인스턴스를 가진다.
- completed 이벤트는 아무런 인스턴스를 가지지 않고 단순히 이벤트를 종료시킨다.
2. Observable의 생성
just
오직 하나의 Element를 포함하는 Observable Sequence를 생성
let observable: Observable<Int> = Observable<Int>.just(1)
let observable: Observable<String> = Observable<String>.just("1")
of
가변적인 element를 포함하는 Observable Sequence를 생성
let observable: Observable<Int> = Observable<Int>.of(1,2,3,4,5)
element가 많다고해서 배열이 아니고 타입을 보시면 Int타입입니다. 가변적으로 element들을 넣을 수 있습니다.
인자로 Array를 넣게 되면 단일 요소로 가지게 됩니다. 타입은 Observable<[Int]>
let observable: Observable<[Int]> = Observable<[Int]>.of([1,2,3])
// [1,2,3]
from
array의 요소들로 Observable Sequence 생성
타입은 Observable <Int> 이고 오직 Array만 취합니다.
let observable: Observable<Int> = Observable<Int>.from([1,2,3,4,5])
//1
//2
//3
//4
//5
empty
요소를 가지지 않는 Observable
empty연산자를 통해 .completed 이벤트만 방출하게 됩니다.
let observable = Observable<Void>.empty()
Observable은 반드시 특정 타입으로 정의되어야 하는데 타입 추론을 할 수 없으므로 Void는 적절한 타입.
never
empty와는 반대로 never연산자가 있습니다.
never는 이벤트를 방출 조차 하지 않습니다.
let observable = Observable<Any>.never()
range
start 부터 count크기 만큼의 값을 갖는 Observable을 생성한다.
let observable = Observable<Int>.range(start: 3, count: 6)
//3
//4
//5
//6
//7
//8
repeatElement
지정된 element를 계속 Emit 한다.
let observable = Observable<Int>.repeatElement(3)
//3
//3
//3
//3
//3
.
.
.
위의 코드는 3초마다 0부터 숫자가 증가하게 된다.
create
Observer에 직접 Event를 방출합니다.
Observable<Int>.create({ (observer) -> Disposable in
observer.onNext(3)
observer.onNext(4)
observer.onNext(5)
observer.onCompleted()
return Disposables.create()
})
//print
3
4
5
Completed
3. Observable 구독
Observable은 실제로 sequence 정의일 뿐이다. Observable은 구독되기 전에는 아무런 이벤트도 보내지 않습니다. 즉 위의 예제 코드들은 subscribe되기 전까진 값이 실행되지 않았을 것입니다.
위의 Observable 생성 코드들에 대해 subscribe 해보겠습니다.
subscribe
let observable = Observable<Int>.range(start: 3, count: 6)
observable.subscribe(onNext: { (element) in
print(element)
})
// print
3
4
5
6
7
8
completed
4. Disposing and Terninating
dispose()
disposing은 구독을 취소하여 Observable을 수동적으로 종료시킨다.
//1
let observable = Observable.of(1, 2, 3)
//2
let subscription = observable.subscribe({ num in
print(num)
})
subscription.dispose()
- Int 타입의 Observable을 생성
- 이 Observable을 구독. 여기서는 subscripe를 이용해 Disposable을 리턴하도록 한다.
여기서 구독을 취소하고 싶으면 dispose()를 호출하면 된다. 구독을 취소하거나 dispose 한 뒤에는 이벤트 방출이 정지되겠죠?
DisposeBag()
각각의 구독에 대해서 일일히 관리하는 것은 효율적이지 못하기 때문에, RxSwift에서 제공하는 DisposedBag 타입을 이용할 수 있습니다.
즉, Disposable 들을 모아놨다가 한번에 처분하는 방식 입니다.
// 1
let disposeBag = DisposeBag()
// 2
Observable.of("A", "B", "C")
.subscribe{
print($0)
}
.disposed(by: disposeBag) // 3
- 한번에 모아서 처리할 dispose bag을 만들어줍니다.
- observable을 만든다
- subscribe로부터 방출된 리턴 값을 disposeBag에 추가한다.
observable에 대해 subscribing 하고 이 것을 즉시 dispose bag에 추가하는 방식의 패턴으로 자주 사용하시게 될것입니다.
5. Trait
Trait은 일반적인 Observable보다 좁은 범위의 Observable
사실 Observable만 사용해도 됩니다. 즉, Trait은 옵션으로 사용할 수 있습니다.
하지만 Trait의 사용 목적은 코드의 가독성을 높일 수 있습니다.
종류 : Single, May, Completable
Single
Single은 Obvservable의 한 형태이지만, Observable처럼 존재하지 않는 곳에서부터 무한대까지의 임이의 연속된 값들을 배출하는 것과는 달리, 항상 한 가지 값 또는 오류 알림 둘 중 하나만 배출한다.
Single은 .success(value)나 .error 이벤트를 방출 합니다.
.success(value)는 .next + .completed 이벤트의 결합이라고 볼 수 있습니다.
예를들어 한가지 프로세스인 데이터 다운로드의 로직에서 사용할 수 있겠죠
Completable
Completable은 .completed나 .error를 방출 하고, 어떠한 값을 방출 하진 않습니다!
연산이 성공적으로 완료되었는지 확인하고 싶을 때 사용합니다.
Maybe
Maybe는 Single 과 Completable을 섞어 놓은 형태이고,
.success(value) , .completed , .error 를 방출합니다.
프로세스가 성공, 실패 여부와 더불어 출력된 값도 방출 할 수 있습니다.
let disposeBag = DisposeBag()
enum FileReadError: Error {
case fileNotFound
}
func findFilePath(from name: String) -> Single<String> {
return Single.create(subscribe: { (single) -> Disposable in
guard let path = Bundle.main.path(forResource: name, ofType: "txt") else {
single(.error(FileReadError.fileNotFound))
return Disposables.create()
}
single(.success(path))
return Disposables.create()
})
}
findFilePath(from: "김승진")
.subscribe(onSuccess: { (result) in
print(result)
}, onError: { (error) in
print(error)
})
'RxSwift' 카테고리의 다른 글
[RxSwift] filter vs skip operator (0) | 2019.09.14 |
---|---|
[RxSwift] Subject와 Relay - (3) (0) | 2019.07.25 |
[RxSwift] RxSwift의 기본 개념 - (1) (0) | 2019.07.25 |