iOS/RxSwift

[RxSwift] Subject에 관하여

Chafle 2022. 10. 25. 18:46
반응형

subject는 관찰자일 뿐만아니라 관찰할 수 있다.

= subscribe 할 수 있고 event를 내보낼 수 있다.

 

subject가 event를 받고 그 결과를 subscriber에게 전달

 

 

subject의 종류
  • Publish Subject
  • Behavior Subject
  • Replay Subject
  • Variable
  • Behavior Relay

 

 

1. Published Subject

 

기본적인 서순..

event를 생성/ event 렌더링 / event트리거 -->> subscription 생성

 

초기값이 필요가 없다.

 

let disposeBag = DisposeBag()

// published subject

let subject = PublishSubject<String>()

subject.onNext("Issue 1")

subject.subscribe { event in 
    print(event)
}

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

//subject.dispose()
subject.onCompleted()
subject.onNext("Issue 4")

publishsubject는 말 그대로 발행하는 친구인데

위에서 말했듯 서순은 이벤트가 생성, 트리거 된 이후에나 발행 가능하다.

 

subscribe 를 생성하기 이전에 publish는 생성되지 않을 것..

 

 

그렇기 때문에 Issue 1은 아직 생성되지 않은 모습..

 

그 이후

 

dispose이후 publish한 Issue 4는 발행되지 않고

여기 onCompleted 이후 publish는 발행되지 않는다.

 

 


 

 

2. Behavior Subject

 

Published Subject와 비슷하지만 초기값을 전달해야 한다.

 

subscribe할 때 초기값 또는 마지막 값을 준다.. 이게 무슨말이냐면

 

 

let disposeBag = DisposeBag()


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

subject.subscribe { event in
    print(event)
}

subject.onNext("Issue 1")

 

 

 

위와같이 초기값을 미리 설정해줬기 때문에 초기값과, Issue1이 출력된다.

 

 

그렇다면 아래는?

let disposeBag = DisposeBag()

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

subject.onNext("Edited Issue")
subject.onNext("Edited Issue2")
subject.onNext("Last Issue")

subject.subscribe { event in
    print(event)
}

subject.onNext("Issue 1")

 

초기값이 있고, subscribe 이전에 여러 값들이 있고

subscribe이후에 Issue 1이 있다면..

 

 

초기값으로 설정된 값이 아니고 LastValue인 Last Issue와 

subscribe이후인 Isuue 1이 출력된다.

 


3. Replay Subject

 

얘는 버퍼크기를 기반으로 하는 Subject인데

마지막에서 버퍼 크기만큼의 값을 반환한다.

 

아래와같은 경우 어떻게 출력이 될까

 

 

let disposeBag = DisposeBag()

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

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

subject.subscribe {
    print($0)
}

bufferSize를 2로 설정했으므로

마지막 두 개의 값을 반환한다.

 

 

그럼 아래와같은 경우라면?

 

let disposeBag = DisposeBag()

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

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

subject.subscribe {
    print($0)
}

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

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

 

 


4. Variables

 

값의 속성(Property)로 값에 엑세스를 할 수 있다.

 

let disposeBag = DisposeBag()

let variable = Variable("Initial Value")

variable.value = "Hello Cahssi"

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

 

근데 출력값을 보면 경고가 뜨는데

이제 향후 사용중단이 되기 때문에 variable대신 behaviorRelay를 사용하라는 것이다. (자세한 건 아래에서 확인하자)

 

 

Variable은 문자열로 Initialize해서 시작할 수 있다.

let disposeBag = DisposeBag()

let variable = Variable([String]())

variable.value.append("item 1")

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

 

 

 

Variable의 장점은 변경하기 용이하다

Observable로 converting이후에도 아래와 같이 변경 가능하다

let disposeBag = DisposeBag()

let variable = Variable([String]())

variable.value.append("item 1") 

variable.asObservable()
    .subscribe {
        print($0)
    }
    
variable.value.append("item 2")

 

 

 

 

 


5. Behavior Relay

 

우리는 위의 caution처럼 variable 보다 BehaviorRelay를 대체해서 써야하는데

 

RxCocoa가 있어야 한다.

 

 

 

이어서 RxCocoa설치해주고

 

 

import UIKit
import RxSwift
import RxCocoa

let disposeBag = DisposeBag()

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

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

relay.value = "Hello Chassi"

 

 

이제 실제로 Variable과 같이 진행을 해보면

 

이 값은 읽기 전용으로 변경할 수 없다.

 

하지만 방법은 있는데, 새 값을 받아서 할당할 수 있는 함수가 있다.

 

바로 accept..

 

 

let disposeBag = DisposeBag()

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

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

relay.accept("Hello Chassi")

 

 

배열로 들어가게 되면

let relay = BehaviorRelay(value: [String]())

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

 

 

 

잘 들어가지는데, variable처럼 배열에 element를 넣으려고 .append를 사용하면

let relay = BehaviorRelay(value: [String]())

relay.value.append("Item 1")
relay.asObservable()
    .subscribe{
        print($0)
    }

또 역시 같은 오류가 난다.

해결 방법은

 

 

1. 위에서와 같이 accept를 사용

let relay = BehaviorRelay(value: [String]())

relay.accept(["Item 1"])
relay.asObservable()
    .subscribe{
        print($0)
    }


 

그런데 초기값을 설정해주는 것이 아니고 아래와 같이 이미 초기값은 Array형태로 있으면서, accept로 추가하려고 해주면

lastvalue밖에 출력되지 않는다.

 

let relay = BehaviorRelay(value: ["Item 1"])

relay.accept(["Item 2"])
relay.asObservable()
    .subscribe{
        print($0)
    }

 

 

그렇다면

 

2.  초기값을 넣어주는 것이 아니고 이미 있는 배열에 추가하고 싶을 때는?

 

(a) relay.value + []를 사용한다.

(b) var로 relay.value를 객체로 만든 이후에 append하고 relay.accept를 통해 (value)를 추가하는 법

 

결국엔 같은 원리다.

 


(a)

let relay = BehaviorRelay(value: ["Item 1"])

relay.accept(relay.value + ["Item 2"])
relay.asObservable()
    .subscribe{
        print($0)
    }

 

 

 

(b)

 

let relay = BehaviorRelay(value: ["Item 1"])

var value = relay.value
value.append("Item2")
value.append("Item3")
relay.accept(value)

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

 

 

반응형