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

next

하나의 sequence를 나타내는 그림이다

화살표는 sequence의 lifetime(시간의 흐름)을 나타내고

1, 2, 3은 sequence의 lifetime동안 발생한 next event를 의미한다

completed

completed

sequence의 lifetime동안 3개의 tap이벤트(next event)가 발생했고

오른쪽의 “ ” 모양이 있는데 이것이 completed event에 해당한다

completed가 발생하면 sequence가 종료되고, 더이상 어떤 이벤트도 발생하지 않는다

error

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.neversubscribe()가 실행되지만 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
     */
}

References

[RxSwift] 2. Observables (tistory.com)

[RxSwift] Observable이란 - (2) - jinShine