-
Notifications
You must be signed in to change notification settings - Fork 2
Open
Labels
Description
Ch.2 Observable
A. Observable 이란?
observable=observable sequence=sequence, 비동기적인 이벤트 스트림- Observable 들은 일정 기간 동안 계속해서 이벤트를 생성하며, 이러한 과정을 보통 emitting(방출)이라고 표현
- 이벤트들은 숫자나 커스텀한 인스턴스 등과 같은 값을 가질 수 있으며, 또는 탭과 같은 제스처를 인식할 수도 있다.
B. Observable의 생명주기
1. Next
- Observable은 앞서 설명했던
next이벤트를 통해 각각의 요소들을 방출하는 것. next이벤트를 계속해서 방출할 수 있다
2. Completed
- 이 Observable은 세 개의 tap 이벤트를 방출한 뒤 완전종료됨. 이 것을 앞서 말한 대로
completed이벤트라고 한다. completed이벤트 방출 후 완전 종료
3. Error
- 이 marble diagram에서는 상단의 예시들과 다르게 에러가 발생한 것.
error이벤트 방출 후 완전 종료
/// Represents a sequence event.
/// Sequence grammar:
/// **next\* (error | completed)**
public enum Event<Element> {
/// 어떠한 Element 인스턴스를 가진다
/// Next elemet is produced.
case next(Element)
/// Swift.Error 인스턴스를 가진다
/// Sequence terminated with an error.
case error(Swift.Error)
/// 아무런 인스턴스를 가지지 않고 단순히 이벤트를 종료
/// Sequence completed successfully.
case completed
}C. Observable 만들기 - 생성
1. create
AnyObserver 타입의 emitter 방출, next, error, completed의 세 가지 타입의 유형으로 switch 문을 통해 제어
Observable<String>.create { observer in
observer.onNext("A")
observer.onCompleted()
observer.onNext("B")
return Disposables.create()
}.subscribe(onNext: { print($0) },
onError: { print($0) },
onCompleted: { print("Completed") },
onDisposed: { print("Disposed") }
)
.disposed(by: disposeBag)
//A
//Completed
//Disposed2. just
오직 하나의 요소를 포함하는 Observable sequence를 생성
3. of
주어진 값들의 타입추론을 통해 Observable sequence를 생성
어떤 array를 observable array로 만들고 싶을 때 사용
let observable2 = Observable.of(1, 2, 3)
observable2.subscribe { event in
print(event)
}
let observable3 = Observable.of([1, 2, 3])
observable3.subscribe { event in
print(event)
}
//next(1)
//next(2)
//next(3)
//completed
//next([1, 2, 3])
//completed4. from
일반적인 array 각각 요소들을 하나씩 방출, 오직 array 만 랩핑 가능
let observable4 = Observable.from([1, 2, 3, 4, 5])
observable4.subscribe { event in
print(event)
}
observable4.subscribe { event in
if let element = event.element {
print(element)
}
}
//next(1)
//next(2)
//next(3)
//next(4)
//next(5)
//completed
//1
//2
//3
//4
//55. 기타
- .empty(): 요소를 하나도 가지지 않는 Observable 생성 - onCompleted 이벤트만 방출
- .never(): 요소를 하나도 가지지 않으며 - 아무런 이벤트도 방출하지 않음
- .range(start: i, count: j): start 부터 count크기 만큼의 값을 갖는 Observable을 생성, 이벤트를 방출
D. Observable 구독
- Observable은 실제로 sequence 정의일 뿐이다. Observable은 subscriber, 즉 구독되기 전에는 아무런 이벤트도 보내지 않는다 - 즉, 해당 스트림을 저장하고 있는 상태
- subscription이 Observable이 이벤트들을 방출하도록 해줄 방아쇠 역할
- .subscribe()를 통해서 .next, .error, .completed 이벤트를 받고 Disposable 타입의 인스턴스를 리턴
E. Disposing과 종료
- Observable에 대한 구독을 취소함으로써 Observable을 수동적으로 종료하는 것이 가능
- 메모리 관리와 항상 연관지어 생각
- .dispose()를 통해서 구독을 취소함으로써 Observable을 수동적으로 종료
- DisposeBag()을 통해서 .disposed(by:) 메서드로 추가된 disposables 타입들을 일괄적으로 관리하여 종료하는 것이 가능 - deinit 활용
F. observable factory 만들기
lazy var 같은 느낌처럼, subscribe 될 때, .deferred() 를 활용하여 내부 블록이 실행되어 리턴 값인 Observable를 활용하는 것이 가능
example(of: "deferred") {
let disposeBag = DisposeBag()
// 1
var flip = false
// 2
let factory: Observable<Int> = Observable.deferred{
// 3
flip = !flip
// 4
if flip {
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()
}
}
/* Prints:
123
456
123
456
*/G. Traits 사용
- Trait은 일반적인 Observable 보다 좁은 범위의 Observable 으로 선택적으로 사용할 수 있다.
- 사이드 이펙트를 공유하지 않는다
- Trait을 사용해서 코드 가독성을 높일 수 있다.
- asObservable()을 사용하면 Observable로 사용가능
1. Single
- Single은
.success(Value)이벤트 또는.error이벤트를 한번만 방출 success=.next+.completed- 파일 저장, 파일 다운로드, 디스크에서 데이터 로딩 같이, 기본적으로 값을 산출하는 비동기적 모든 연산에 유용
- HTTP 요청과 같이 하나의 response나 에러만을 다루는 경우에 주로 사용
사용 예시
PhotoWriter.save(_)에서 처럼, 정확히 한가지 요소만을 방출하는 연산자를 래핑할 때Observable대신Single을 생성하여PhotoWriter의save(_)메소드를 업데이트 할 수 있다.
- signle sequence가 둘 이상의 요소를 방출하는지 구독을 통해 확인하면 error가 방출될 수 있다.
- 이 것은 아무 Observable에
asSingle()를 붙여Single로 변환시켜서 확인할 수 있다.
- 이 것은 아무 Observable에
var disposeBag = DisposeBag()
enum CustomError: Error {
case decodeFail
}
func getRepo(_ repo: String) -> Single<[String:Any]> {
return Single<[String:Any]>.create { single in
let task = URLSession.shared.dataTask(with: URL(string: repo)!) { data, response, error in
if let error = error {
single(.failure(error))
return
}
guard let data = data,
let json = try? JSONSerialization.jsonObject(with: data, options: .mutableLeaves),
let result = json as? [String:Any] else {
single(.failure(CustomError.decodeFail))
return
}
single(.success(result))
}
task.resume()
return Disposables.create {
task.cancel()
}
}
}
getRepo("repoURL")
.subscribe { event in
switch event {
case .success(let result):
print(result)
case .failure(let error):
print(error)
}
}.disposed(by: disposeBag)2. Completable
Completable은.completed또는.error(Error)만을 방출한다.- Operation이 끝났는지 아닌지는 확인이 필요하지만 element를 다루지 않을 때 사용
-
하나 기억해야 할 것은, observable을 completable로 바꿀 수 없다는 것이다.
-
observable이 값요소를 방출한 이상, 이 것을 completable로 바꿀 수는 없다.
-
completable sequence를 생성하고 싶으면
Completable.create({...})을 통해 생성하는 수 밖에 없다. 이 코드는 다른 observable을create를 이용하여 생성한 방식이랑 매우 유사하다. -
Completeble은 어떠한 값도 방출하지 않는다는 것을 기억해야 한다. 솔직히 이런게 왜 필요한가 싶을 것이다.- 하지만, 동기식 연산의 성공여부를 확인할 때
completeble은 아주 많이 쓰인다.
- 하지만, 동기식 연산의 성공여부를 확인할 때
-
작업했던
Combinestagram예제를 통해 생각해보자.- 유저가 작업할 동안 자동저장되는 기능을 만들고 싶다.
- background queue에서 비동기적으로 작업한 다음에, 완료가되면 작은 노티를 띄우거나 저장 중 오류가 생기면 alert을 띄우고 싶다.
-
저장 로직을
saveDocumet() -> Completable에 래핑했다고 가정해보자. 다음과 같이 표현할 수 있다.saveDocument() .andThen(Observable.from(createMessage)) .subscribe(onNext: { message in message.display() }, onError: { e in alert(e.localizedDescription) })
andThen연산자는 성공 이벤트에 대해 더 많은 completables이나 observables를 연결하고 최종 결과를 구독할 수 있게 한다.
3. Maybe
Maybe는Single과 비슷하지만 유일하게 다른 점은 성공적으로 complete 되더라도 아무런 값을 방출하지 않을 수도 있다는 것이다.
- 문자열을 생성하는 함수가 있다. 함수를 통해서 Maybe<String>타입의 값을 방출하고 그 값을 구독하여 활용하는 예시다.
- maybe에 세 가지 경우 .success, .completed, .error 가 있다.
- 맨 위의 success를 뱉으면 나머지 두개는 방출되지 않는다.
- subscribe한 부분을 보면 element를 뱉으면 completed가 알아서 된다
- 그냥 Observable에서 observer.on(next:)로 보내고 따로 complete을 해야하는거와 비교된다.
- 그냥 Observable은 onDispose 하기 위해선 complete가 필요하다..
asSingle처럼, 어떤 Observable을Maybe로 바꾸고 싶다면,asMaybe()를 쓸 수 있다.
var disposeBag = DisposeBag()
enum CustomError: Error {
case maybeFail
}
func generateString() -> Maybe<String> {
return Maybe<String>.create { maybe in
maybe(.success("RxSwift"))
maybe(.completed)
maybe(.error(CustomError.maybeFail))
return Disposables.create {}
}
}
generateString()
.subscribe { maybe in
switch maybe {
case .success(let element):
print("completed with element")
case .completed:
print("completed with no element")
case .error(let error):
print("completed with error", error)
}
}.disposed(by: disposeBag)




