Skip to content

Observable  #17

@simoniful

Description

@simoniful

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
//Disposed

2. 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])
//completed

4. 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
//5

5. 기타

  • .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을 생성하여 PhotoWritersave(_) 메소드를 업데이트 할 수 있다.
  • signle sequence가 둘 이상의 요소를 방출하는지 구독을 통해 확인하면 error가 방출될 수 있다.
    • 이 것은 아무 Observable에 asSingle()를 붙여 Single로 변환시켜서 확인할 수 있다.
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

  • MaybeSingle과 비슷하지만 유일하게 다른 점은 성공적으로 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)

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions