[RxSwift] Subjects

silverCastle·2022년 10월 12일
0
post-thumbnail

Observable만큼 중요한 개념이 Subject이다. 누군가에게 event를 받고 subscriber에게 그 결과를 전달한다.
Subject의 종류로는 Publish Subject, Behavior Subject, Replay Subject, Variable, BehaviorRelay가 있다. 하나하나씩 살펴보자.

✍️ Publish Subjects

Publish Subject는 subscribe한 시점 이후에 발생되는 event만 전달받아 subscriber에게 그 결과를 전달한다.
먼저 코드를 보면서 이해해보자. 코드는 같은 파일에 있다고 가정하며 설명하는 순서대로 코드가 작성되어 있다.

import UIKit
import RxSwift

let disposeBag = DisposeBag()

let subject = PublishSubject<String>()

subject.onNext("Issue 1")

위 코드와 같이 PublishSubject를 생성하면 String 타입의 event만 방출할 수 있다.
이대로 실행하면 아무일도 일어나지 않는다. Why? subscriber가 없기 때문.
이 의미는 다음과 같다. 내가 잡지 회사를 갖고 있고 새로운 잡지를 발행했는데 구독자가 아무도 없기 때문에 아무것도 발생하지 않는거와 동일하다.
그럼 subscribe을 한다면 원하는 결과가 나올까?

subject.subscribe { event in
    print(event)
}

지금도 별 차이를 못 느낄 것이다. 즉, call한 subscription을 여전히 못 볼 것이다.
Why? event를 create한 이후에 subscription을 create했기 때문.
즉, Publish Subject는 현재 나를 subscribe하는 subscriber들에게만 event를 방출한다.
그럼 subscription을 create한 이후에 event를 생성하면 원하는 결과를 얻을 수 있다.

subject.onNext("Issue 2")
subject.onNext("Issue 3")

결과

next(Issue 2)
next(Issue 3)

그 후, dispose하고 event를 생성한다면 어떻게 될까?

subject.dispose()

subject.onNext("Issue 4")

당연하게도 구독을 끊었기 때문에 당연히 이런 event는 무시된다.
만약 잡지를 구독하다가 구독을 해지했는데 새로운 잡지가 발행된다면 받을 수 있을까? 당연히 받을 수 없다.

subject는 value를 방출할 수 있고 observe 할 수 있기 때문에 subscriber이고 observer 역할을 하는 것을 잊지 말자.

✍️ Behavior Subjects

Publish Subject와 매우 비슷하지만 한가지 차이점이 있다. 초기화할 때 반드시 초기값을 줘야한다. Why? subscribe할 때 반드시 가장 최신의 value를 방출하기 때문. 즉, subscribe가 발생하면 발생한 시점 이전에서 가장 최신의 event를 전달받는다.

let subject = BehaviorSubject(value: "Initial value")

subject.subscribe { event in
    print(event)
}

결과

next(Initial value)

그 이후에 event를 생성하면?

subject.onNext("Issue 1")

결과

next(Initial value)
next(Issue 1)

subscribe할 때를 기준으로 가장 최신의 event도 같이 방출하는 것을 알 수 있다.

그럼 behavior subject의 장점은? 새로운 subscriber가 가장 최신의 event만을 알아야할 때 좋다는 것이다.

✍️ Replay Subjects

Replay Subject는 우리가 설정한 buffer size를 바탕으로 event를 replay한다.

let subject = ReplaySubject<String>.create(bufferSize: 2)

이 의미는 새로운 subscriber가 이 subject를 subcribe할 때, 자동적으로 subject에 의해 방출된 가장 최신의 value 2개를 replay한다는 것이다.

subject.onNext("Issue 1")
subject.onNext("Issue 2")
subject.onNext("Issue 3")

어떤 일이 일어날까? 우리는 이 subject에 대한 subscription이 없기 때문에 아무일도 안일어난다.
아시다시피, 단지 event만 방출한거다. 하지만 이것에 대해 듣는 사람은 아무도 없다.
So, 이것을 subscribe할거다.

subject.subscribe {
    print($0)
}

결과

next(Issue 2)
next(Issue 3)

buffer size만큼 나오는 것을 알 수 있다.
그럼 새로운 subscriber를 만들면?

subject.onNext("Issue 4")
subject.onNext("Issue 5")
subject.onNext("Issue 6")

print("[Subscription 2]")
subject.subscribe {
    print($0)
}

결과

[Subscription 2]
next(Issue 5)
next(Issue 6)

새로운 subscriber를 위해 가장 최신의 value를 replay한다.

So, replay subject는 만약 buffer size에 기반해 replay를 원할 때 좋다. 예를 들어 최근 검색한 기록 중 몇개만 보고싶을 때이다.

✍️ Variables

value property를 사용하여 Subject의 현재 값에 접근할 수 있다. 또한 value property를 통해 새로운 값을 추가할 수 있다. onNext(_: ) 메서드는 사용하지 않는다.
변수를 observable로 변환한 다음 변수를 구독할 수 있다. 즉, variable은 subject가 아니므로 asObservable()을 사용하여 subject에 접근한 뒤 subscribe한다.

let variable = Variable("Initial Value")

variable.value = "Hello World"

variable.asObservable()
    .subscribe {
        print($0)
    }

결과

ℹ️ [DEPRECATED] `Variable` is planned for future deprecation. Please consider `BehaviorRelay` as a replacement. Read more at: https://git.io/vNqvx
next(Hello World)

결과에서 알 수 있듯이, 주목해야할 점은 Variable이 조만간 사라질 계획이다는 것이다. Variable의 대체로 BehaviorRelay를 사용하라고 한다.

String 타입뿐만 아니라 Array 타입도 가능하다.

let variable = Variable([String]())

variable.value.append("Item 1")

variable.asObservable()
    .subscribe {
        print($0)
    }

결과

ℹ️ [DEPRECATED] `Variable` is planned for future deprecation. Please consider `BehaviorRelay` as a replacement. Read more at: https://git.io/vNqvx
next(["Item 1"])

✍️ BehaviorRelay

pod 'RxCocoa', '~> 4.0'

BehaviorRelay를 사용하기 위해 위와 같이 의존성을 추가해주고 RxCocoa를 import해준다.
코드를 보자.

let relay = BehaviorRelay(value: "Initial Value")

relay.asObservable()
    .subscribe {
        print($0)
    }

결과

next(Initial Value)

Variable과 차이가 없어 보인다.
But, BehaviorRelay의 value를 바꿀 때 그 차이를 알 수 있다.

relay.value = "Hello World"

결과

Cannot assign to property: 'value' is a get-only property

value가 읽기 전용이므로 변경할 수 없다는 에러를 내뱉는다.

그럼 어떻게 해야 할까? 기존 값을 변경할 수는 없지만 새로운 값을 할당할 수 있다.

relay.accept("Hello World")

0개의 댓글