[iOS | RxSwift] Observable

minji0801·2022년 1월 11일
0

RxSwift

목록 보기
2/6
post-thumbnail

개요

RxSwift가 무엇인지 공부하며 처음 접한 Observable가 개념상 무엇인지는 조금 알겠지만, 실제로 어떻게 다루는 건지는 잘 모르겠다.
Observable에 대해 복습하고 Xcode에서 실습해 보는 시간을 가져보자.

Observable에 대해 처음 들어보거나 잘 모른다면 빠른 이해를 돕기 위해 RxSwift, 도대체 뭘까?를 먼저 보는 것을 추천한다.


⁉️ Observable, 뭐였지?

Observable은 Swift에서 기본으로 제공하는 Sequence와 동일하다.
비동기적으로 이벤트를 생성해낸다는 중요한 특징이 있다.

시간 흐름에 따라 값을 표시하는 marble diagram을 이용하면 개념을 잘 이해할 수 있다. (참고 : RxMarbles)
또한, 공식 문서에도 Oservable에 대해 잘 정리되어 있다. (참고 : RxSwift - Documentation : Observable)

Observable은 이렇게 3가지 유형의 이벤트만 생성할 수 있다.

enum Event<Element>  {
    case next(Element)      // next element of a sequence
    case error(Swift.Error) // sequence failed with error
    case completed          // sequence terminated successfully
}

바로 이게 marble diagram인데, Observable은 next 이벤트를 통해 요소들을 방출한다.
그리고 completed 이벤트는 마지막에 수직선( | )으로 끝내고, error 이벤트는 마지막에 X로 끝낸다.

--1--2--3--4--5--6--| // terminates normally
--a--b--a--a--a---d---X // terminates with error
---tap-tap-------tap--->

즉, 첫 번째는 compledted 이벤트를 방출해서 완전히 종료되었고, 두 번째는 error 이벤트를 방출해서 완전히 종료된 것이다. 마지막은 종료되지 않은 무한한 Observable이다.


💻 실습해보기

RxSwift 설치

먼저, CocoaPods으로 RxSiwft를 설치한다.
참고 : RxSwfit - Documentation : Installation

CocoaPods

    pod 'RxSwift', '6.2.0'
    pod 'RxCocoa', '6.2.0'

그리고 나서 Foundation과 RxSwift 라이브러리를 import 시켜준다.

import Foundation
import RxSwift

just, of, from

just, of, from으로 Observable을 만들 수 있는데, just는 하나의 요소만 방출한다.
참고로, 각 요소가 방출되는지 확인하기 위해서는 반드시 subscribe를 해야한다.

// just
Observable<Int>.just(1)
    .subscribe(onNext: {
        print($0)
    })
// 1

of는 하나 이상의 요소를 방출한다.
그런데 두번째 of 예시처럼 하나의 배열을 요소로 넣게 되면 배열의 요소가 아닌 배열 자체를 방출한다.
즉, Observable.just([1, 2, 3, 4, 5]) 와 같은 것이다.

// of
Observable<Int>.of(1, 2, 3, 4, 5)   // of() : 하나 이상의 요소 방출
    .subscribe(onNext: {
        print($0)
    })
// 1
// 2
// 3
// 4
// 5
// of
Observable.of([1, 2, 3, 4, 5])
    .subscribe(onNext: {
        print($0)
    })
// [1, 2, 3, 4, 5]

from은 Array만 받아서 Array의 각 요소를 하나씩 방출한다.

// from
Observable.from([1, 2, 3, 4, 5])
    .subscribe(onNext: {
        print($0)
    })
// 1
// 2
// 3
// 4
// 5

subscribe

우선, 아래와 같이 subscribe을 이용하면 이벤트를 방출할 수 있다.

Observable.of(1, 2, 3)
    .subscribe {
        print($0)
    }
// next(1)
// next(2)
// next(3)
// completed

그리고 이렇게 원하는 이벤트만 방출할 수도 있다. 이 예시는 onNext와 같은 경우이다.

Observable.of(1, 2, 3)
    .subscribe {
        if let element = $0.element {
            print(element)
        }
    }
// 1
// 2
// 3
Observable.of(1, 2, 3)
    .subscribe(onNext: {
        print($0)
    })
// 1
// 2
// 3

empty, never, range

빈 Observable을 만들고 싶다면 empty를 이용하면 된다.
비어있는 Observable는 즉시 종료하고 싶은 Observable이나 0개의 요소를 가지는 Observable을 return 할 때 사용할 수 있다.

그리고 Observable의 타입을 적어주지 않으면 아무런 이벤트가 방출되지 않지만, Void 타입을 적어주면 completed 이벤트가 방출된다.

Observable<Void>.empty()
    .subscribe {
        print($0)
    }
// completed

never는 completed 조차 방출되지 않는다.
그래서 subscribe 되었는지 확인하려면 debug()를 통해서 확인할 수 있다.

Observable.never()
    .debug("never")
    .subscribe(
        onNext: {
            print($0)
        },
        onCompleted: {
            print("Completed")
        }
    )
// 2022-01-11 11:55:42.816: never -> subscribed

range는 start부터 count까지 값이 하나씩 증가하는 Observable을 만들 수 있다.

Observable.range(start: 1, count: 9)
    .subscribe(onNext: {
        print("2*\($0)=\(2*$0)")
    })
// 2*1=2
// 2*2=4
// 2*3=6
// 2*4=8
// 2*5=10
// 2*6=12
// 2*7=14
// 2*8=16
// 2*9=18

dispose

dispose는 subscribe를 취소하는 것이다.
아래 예시에는 별다를 게 없지만, 무한한 요소를 가진 Sequence라면 반드시 dispose를 호출해야 completed가 방출된다.

Observable.of(1, 2, 3)
    .subscribe {
        print($0)
    }
    .dispose()
// next(1)
// next(2)
// next(3)
// completed

이렇게 일일이 dispose로 관리하는 것은 매우 비효율적이고 메모리 누수가 발생할 수 있기 때문에 DisposeBag을 사용한다.
DisposeBag이 Disposable을 가지고 있는데, 이는 할당 해제하려고 할 때마다 dispose를 호출한다.
즉, DisposBag에 추가해서 자신이 할당 해제할 때 해당하는 모든 subscribe를 해제시키는 것이다.

그냥 간단하게 말해서 dispose 관리를 효율적으로 하기 위한 것이다.

let disposeBag = DisposeBag()
Observable.of(1, 2, 3)
    .subscribe {
        print($0)
    }
    .disposed(by: disposeBag)

create

create는 Observer를 받아서 Disposable를 return 하는 형태로 Observable을 만들 수 있다.
아래 예시에서 onCompleted()로 이벤트가 종료되었기 때문에 onNext(2)는 방출되지 않은 것이다.

Observable.create { observer -> Disposable in
    observer.onNext(1)
    observer.onCompleted()
    observer.onNext(2)
    return Disposables.create()
}
.subscribe {
    print($0)
}
.disposed(by: disposeBag)
// 1

이번에는 에러를 포함하는 create 예시이다.
onError(error.anError)에서 에러를 방출하며 이벤트가 종료되었고, dispose()도 실행되었다.

여기서 만약에 error, completed, dispose를 없애고 오로지 next 이벤트만 있다면 종료되지 않아 메모리 누수가 발생할 수 있다. 따라서 반드시 dispose 해줘야 한다.

enum error: Error {
    case anError
}
Observable.create { observer -> Disposable in
    observer.onNext(1)
    observer.onError(error.anError)
    observer.onCompleted()
    observer.onNext(2)
    return Disposables.create()
}
.subscribe(
    onNext: {
        print($0)
    }, onError: {
        print($0.localizedDescription)
    }, onCompleted: {
        print("completed")
    }, onDisposed: {
        print("disposed")
    }
)
.disposed(by: disposeBag)
// 1
// The operation couldn’t be completed. (__lldb_expr_25.error error 0.)
// disposed

deffered

deffered는 Observable Factory를 만드는 것이다.
subscribe를 기다리는 Observable를 만드는 대신에 각 subscribe에게 새롭게 Observable 항목을 제공한다.

아래 예시는 Observable Factory 안에 있는 Observable 묶음 중에서 조건문에 따라 나온다.
즉, deferred는 Observable Factory를 통해서 Observable Sequence를 생성하는 형태인 것이다.

var up: Bool = false
let factory: Observable<String> = Observable.deferred {
    up = !up
    if up {
        return Observable.of("up")
    } else {
        return Observable.of("down")
    }
}
for _ in 0...3 {
    factory.subscribe(onNext: {
        print($0)
    })
        .disposed(by: disposeBag)
}
// up
// down
// up
// down

마무리

이번에는 여러 가지 형태로 Observable을 만드는 연산자에 대해서 알아보았다.
다음에는 좁은 범위의 Observable인 Traits(Single, Maybe, Completable)에 대해서 알아보자.

profile
iOS Developer

0개의 댓글