[Swift/iOS] RxSwift, RxCocoa

최정은·2023년 10월 30일
0

Swift

목록 보기
27/27

RxSwift (Reactive Extension + Swift) 란?

  • 관찰 가능한 연속성(순차적)형태와 함수형태의 연산자를 이용해서 비동기&이벤트를 위한 코드로 구성된 라이브러리
  • 반응형 프로그래밍 + 스위프트

RxSwift를 왜 사용해야 할까?

Notification, Delegate Pattern, Closure, 옵저버 패턴으로도 반응형 프로그래밍을 구현할 수 있으나, 다음과 같은 장점이 있다.!
1. Thread 처리가 쉽다.

  • MainThread 에서 처리하기, 백그라운드에서 처리하다 다시 메인, 다시 백그라운드, .. 쉽게 처리할 수 있다.
  1. 코드가 깔끔해진다.
  • 가독성이 좋다.
  1. 비동기 이벤트의 흐름을 쉽게 파악하고 작성할 수 있다.
  • RxSwift를 사용하지 않는다면 여러 스레드간의 클로저 이벤트 처리나 콜백지옥으로 코드가 복잡하고 가독성이 떨어질 수 있다.
  1. MVVM Design Pattern과 같이 사용하여 이벤트 중심 프로그램에서 데이터의 바인딩을 쉽게 처리할 수 있다.
  2. Operator를 사용하여 이벤트들을 연산할 수 있다.
  3. 연속된 escaping closure를 피할 수 있다.

반응형 프로그래밍

  • 데이터의 흐름 및 변경사항을 전파하는 데 중점을 둔 프로그래밍 패러다임. 이 패러다임을 사용할 경우, 주변환경/데이터에 변화가 생길 때 연결된 실행 모델들이 이 이벤트를 받아 동작하도록 설계하는 방식이다.
  • 어떠한 이벤트가 발생하면 그것을 구독(subscribing)하고 있던 관찰자(Observer)가 이를 알아채고 UI(또는 Content)를 실시간으로 업데이트해 주는 것이라고 볼 수 있다.

비동기적인 이벤트

  • 버튼이 눌렸을 때, 키보드가 올라 왔을 때, 언제 일어날지 모르는 어떤 이벤트가 발생 했을 때

Observable

  • 관찰이 가능한 흐름으로, 비동기 이벤트의 시퀀스생성할 수 있는 대상
  • Observable은 실제 그 비동기 이벤트가 일어났을 때, 이를 알리기 위해 그 이벤트에 대한 항목(item)을 시퀀스로 방출한다.

Observer

  • Subscribe(onNext:...) 메서드를 통해 Observable구독할 수 있다.
  • Observable에서 항목방출됐을 때, 에러가 났을 때, 이벤트가 종료되었을 때에 대한 처리를 클로저로 작성하여 파라미터로 넘겨줌
public func subscribe (
    onNext: ((Element) -> Void)? = nil,
    onError: ((Swift.Error) -> Void)? = nil,
    onCompleted: (() -> Void)? = nil,
    onDisposed: (() -> Void)? = nil
) -> Disposable

Dispose

  • Subscribe메서드는 구독을 해제할 때 사용하는 Disposable이란 타입의 인스턴스를 반환한다. 리턴된 Disposable타입의 값에 dispose() 메서드를 호출하면, 구독한 Observable에 대해 구독을 해제할 수 있다.
let disposable = button.rx
                         .tap
                         .subscribe(onNext: {})
 
diebutton.rx
         .tap
         .subscribe(onNext: { disposable.dispose() })
  • Observable은 기본적으로, complete이나 error가 발생하기 전까진 계속 이벤트를 방출 시키는데, 이벤트가 더이상 방출되면 안 되는 시점에서 이 리소스를 직접 deinit 해줘야 한다. 만약 이 deinit 해주는 과정을 하지 않으면, 이 리소스는 계속해서 필요할 때마다 이벤트를 방출시키는 메모리 릭(leak)으로 이어진다.

DisposeBag

  • Disposable 객체를 담는 배열
  • DisposeBag을 전역 프로퍼티로 선언해준 경우, 해당 DisposeBag을 담고 있는 인스턴스가 사라질 때 DisposeBag이 deinit되면서 Disposable의 배열을 순회하며 dispose() 메서드를 호출해주기 때문에, 딱히 별도의 작업을 해주지 않아도, 메모리 릭이 나지 않을 수 있다.
Observable<Int>.interval(
           .seconds(1),
           scheduler: MainScheduler.instance
         )
         .subscribe(onNext: {
             print($0)
         })
         .disposed(by: disposeBag)

Observable을 생성하는 방법

1. just

  • 단 1개의 항목(item)만 방출하는 Observable Sequence를 생성합니다.

2. of

  • 1개 이상의 항목(item)을 방출하는 Observable Sequence를 생성합니다.

3. from

  • 파라미터로 오직 하나의 배열만 받으며, 배열의 요소들을 순서대로 방출하는 Observable Sequence를 생성합니다.

4. create

  • 파라미터로 Observer를 매개변수로 받는 클로저를 전달받는 Observable Sequence를 생성합니다.

  • 매개변수로 받은 Observer의 onNext, onCompleted, onError 메서드를 직접 호출할 수 있습니다. 클로저가 끝나기 전에 반드시 onCompleted이나 onError를 정확히 1번 호출해야 하며, 그 이후로는 Observer의 다른 어떤 메서드도 호출해선 안됩니다


Subject

  • Subject는 Observer이자 Observable입니다. Observer이기 때문에 하나 이상의 Observable을 구독할 수 있으며, 동시에 Observable이기 때문에 새로운 항목(item)들을 방출하거나 재방출 합니다.

Observable(unicast) vs Subject(multicast)

Observable(unicast)

  • 구독한 Observer가 몇 개든 간에, 독자적인 실행을 가진다.

Subject(multicast)

  • 하나의 Observable의 실행이 여러 Subscribe에게 공유된다.

Subject의 종류 4가지

1. PublishSubject

  • 구독 이후에 방출된 항목들만 옵저버에게 전달합니다.
let subject = PublishSubject<String>()

subject.subscribe(onNext: {
    print("첫번째 Observer가 받는 항목 : \($0)")
}).disposed(by: disposeBag)
 
subject.onNext("1")
subject.onNext("2")
 
subject.subscribe(onNext: {
    print("두번째 Observer가 받는 항목 : \($0)")
}).disposed(by: disposeBag)
 
subject.onNext("3")

2. BehaviorSubject

  • BehaviorSubject는 구독할 경우, Observable이 가장 최근에 발행한 항목(아무것도 발행하지 않았다면 초기값)을 한번 방출하며, 그 이후부터 Observable에 의해 방출된 항목들을 Observer에게 전달합니다.
let subject = BehaviorSubject<Int>(value: 1)

subject.subscribe(onNext: {
    print("첫번째 Observer가 받는 항목 : \($0)")
}).disposed(by: disposeBag)

3. ReplaySubject

  • 최신 이벤트를 버퍼 사이즈에 맞게 저장하고, Observer가 구독할 시 버퍼에 있는 이벤트를 모두 전달합니다. BehaviorSubject의 역할에서 "두 개 이상"의 이벤트를 저장하고 싶을 때 사용합니다.

4. AsyncSubject

  • AsyncSubject는 completed 이벤트가 전달되기 전까지 어떠한 이벤트도 방출하지 않습니다. completed 이벤트가 전달되면, 가장 최근에 전달된 값을 방출합니다.

RxCocoa란?

  • RxCocoa란 기존 Cocoa Framework에 Rx기능을 합친 RxSwift기반의 라이브러리 입니다.

1. Binder

  • 메인스케쥴러에서 실행된다.
  • 에러 이벤트를 따로 방출하지 않고 로그로만 출력된다.
  • 옵저버타입이기 때문에 새로운 값을 전달할 수 있지만 구독자를 추가할 수는 없다.

bind

  • bind 는 subscribe 와 동일한 기능이라고 말할 수 있다.
  • 하지만 조금 다른점은 RxSwift에서의 subscribeObservable 이 방출하는 Event를 Observer가 subscribe하여 관찰하지만, RxCocoa bindObservable과 Observer의 관계를 하나로 묶는다는 개념이 있다.

* 코드의 차이점

subscribe

       tf.rx.text
            .observe(on: MainScheduler.instance)
            .subscribe(with: self , onNext:  { ( _ , str ) in
                self.lb.text = str
            }).disposed(by: disposBag)

bind

     tf.rx.text
            .bind(to: lb.rx.text)
            .disposed(by: disposBag)

이처럼 UI를 Controll 하는데 있어 Rxcocoa 를 사용하는것이 조금더 우위에 있습니다. (가독성 등에도 차이가 있는듯 하다).

또한 값을 주입하기 때문에 순환참조에서도 벗어날수 있습니다.

bind 는 binder 되어 있는 Property의 값을 전달받아 주입하기 때문에 따로 메모리릭에 대비하지 않아도 됩니다.

2. Traits

  • UI작업시 코드를 쉽고 직관적으로 작성해 사용할 수 있도록 도와주는 특별한 Observable클래스 모음이다.
  • error를 방출하지 않는다.
  • MainScheduler 에서 subscribe 됩니다.
  • Main Thread에서 실행
  • binder에서는 observer가 증가할 때마다 스트림도 함께 증가해서 리소스 낭비가 발생할 수 있지만 signal을 사용하면 observer가 증가할 때 스트림을 새로 만들지 않고 공유하기 때문에 리소스 낭비를 줄일 수 있습니다.
  • Driver : 구독하는 순간 초기, 최신값을 제공
  • Signer : 구독한 이후에 발생한 값만 제공

3. Rx extension

  • 특정 속성을 rx스럽게 사용하고자 할 때, 원하는 속성이 없는 경우 사용한다.

예를 들어 sizeToFit을 rx에서 사용하고 싶다면

extension Reactive where Base: UIView {
	var sizeToFit: Binder<Void> {
    	return Binder(base) { base, _ in
        	base.sizeToFit()
        }
    }
}

다음과 같이 extension을 작성하여

button.rx.sizeToFit()

이렇게 사용할 수 있습니다.


참고 자료
RxSwift 4시간 끝내기 : https://www.youtube.com/watch?v=iHKBNYMWd5I&t=8s
RxSwift 정리 블로그 : https://babbab2.tistory.com/182
RxCocoa 정리 블로그 : https://velog.io/@leejinseong9410/RxSwift-RxCocoa

0개의 댓글