[Combine] Subject

PassthroughSubject와 CurrentValueSubject⛹🏻‍♀️

naljin
9 min readFeb 24, 2020

Subjectpublisher의 일종입니다. 보다시피 Publisher protocol을 착실하게 따르고 있쥬?

protocol Subject : AnyObject, Publisher

차이점은 지금까지와 달리 밖에서 값을 방출할 수 있다는 점입니다.

조금 더 자세히 알아 볼까요?

send(_:)를 통해 stream에 값을 주입할 수 있는 publisher

우리는 지금까지 아래 그림과 같은 예제만 봤어요. 외부에서는 값을 던질 수 없고 Publisher 내부에서 생성되어 나오는 값들만 받아서 처리했죠!

하지만 publisher가 아닌 subject를 사용하면 send(_:) 를 통해 외부에서도 저 1,3,5 로 이어지는 일련의 스트림에 값을 주입할 수 있다는 것입니다!!! 앗 물론 send(_:)Subject protocol에 정의되어 있답니다.

입 벌려 값(2) 들어간다!⛹🏻‍♀️

이것은 존재하는 명령형 코드를 Combine 모델에 적용하는데 유용하다…라고 하는데 여기까지는 무슨 뜻인지 저도 잘 모르겠네요ㅎ

후비적

여하튼 Subject의 종류는 크게 PassthroughSubjectCurrentValueSubject로 나뉘는데요! 이것들을 살펴보면서 Subject에 대한 구체적인 개념을 잡아봅시다!!!!!!!!

PassthroughSubject

downstream subscriber에게elementbroadcast하는 subject

와.. 이게 무슨.. 조사만 한국어인..그런…문장이람…. ? 바로 예제로 들어가 봅시다.

subject.send(“Hello”)로 값을 보내는걸 발견하셨나요?!

이렇게 PassthroughSubject를 사용하면 send(_:) 를 통해 필요에 따라 새로운 값을 게시할 수 있습니다.broadcast니까 해당 subject를 구독하고 있는 모든 subscriber에게 값을 보낼테고.. 그에 맞는 결과가 찍히겠죠?

——— Example of: PassthroughSubject ———
Received value (1) Hello
Received value (2) Hello
Received value (1) World
Received value (2) World

자자 아래 코드를 더 추가해봅시다.

// 5. subscription1의 구독을 취소합니다.
subscription1.cancel()
// 6. 또 다른 값을 전송합니다.
subject.send("Still there?")

자 5번에서 subscription1의 구독을 취소했으니 이제 subscription2 의 구독만 남게됐습니다. 그래서 send(_:)를 통해 값을 보내도 이렇게 한줄만 더 찍힐거예요.

Received value (2) Still there?

아래 코드를 더 추가해봅시다.

//7. finished라는 완료 이벤트를 보냅니다
subject.send(completion: .finished)
//8. 또 다른 값을 전송합니다.
subject.send("How about another one?")

이렇게하고 돌려보면 ..?!

Received completion (2) finished

완료 이벤트만 찍힐 뿐 새로운 값(“How about another one?”)은 찍히지 않습니다. 왜냐고요? 새로운 값을 보내기 전에 send(completion:) 을 통해 완료 이벤트를 보냈기 때문입니다! 완료 이벤트를 받으면 더 이상 다른 완료 이벤트나 값을 받지 않아요🙅🏻‍♀️

이제 subscriber에게elementbroadcast하는 subject 라는 문장이 이해가 되시나요?!

CurrentValueSubject

PassthroughSubject와 달리 초기값최근 발행된 element에 대한 buffer를 갖습니다! 예시를 통해 확인해보아요.

여기서 CurrentValueSubject0이라는 초기값과 함께 생성되었어요! 따라서 콘솔 창에는 아래와 같이 찍힙니다.

——— Example of: CurrentValueSubject ———
0

여기서 3번 주석처리 된 .store(in: &subscriptions)을 잠깐 짚고 넘어가 봅시다.

PassthroughSubject의 예제에서는 subscription을 값으로 저장한 후 취소를 따로 처리했어요.

subscription.cancel()

이런 작업이 여기서는 store(in:) 을 통해 처리됩니다.

in 안에 들어가는 parameter 타입은 Set<AnyCancellable> 으로 이곳에 여러 subscription을 저장할 수 있어요. 저장된 subscription들은 해당 set이 초기화 해체(deinitialized)될 때 같이 자동으로 취소된답니다. 관리가 훨씬 쉬워질 것 같지 않나요?

동일한 set이 업데이트 되도록 하기위해 인자는 in-out parameter를 통해 전달됩니다. 함수 안에서 parameter의 값을 바꾸고 싶으면서, 해당 함수가 끝나고 난 후에도 그 값들을 유지하고 싶다면 사용하는 것이 바로 in-out parameter이기 때문이죠. 더 자세한 내용은 이곳을 참조하세요.

휴.. 많이도 돌아왔다! 이제 새 값을 발행해봅시다. PassthroughSubject 와 마찬가지로 send(_:)를 호출해서 새 값을 발행할 수 있습니다.

subject.send(1)
subject.send(2)

이렇게 발행된 값은 현재 값으로도 설정됩니다. 현재 값은 subjectvalue값으로 확인할 수 있는데요! subject.send(2) 아래 코드를 추가해봅시다.

print(subject.value)

콘솔 확인!

——— Example of: CurrentValueSubject ———
0 //초기 값
1 //subject.send(1)로 받은 값
2 //subject.send(2)로 받은 값
2 //print(subject.value)로 확인한 현재 값

초기 값 0, 발행된 값 1, 2에 이어서 현재 값 2 가 찍힌 것을 확인할 수 있습니다.

send(_:) 말고 value에 새 값을 직접 할당하는 방법을 통해서도 값을 발행하고 현재 값을 변경 할 수 있습니다. 이렇게 말이죠!

subject.value = 3

다만 subject.value = .finished처럼 완료 이벤트는 value 속성 값 할당을 통해 주입할 수 없습니다. 오직 값만 주입 가능하다는 것!💉

완료 이벤트를 발행하고 싶으면 PassthroughSubject 와 같은 방법을 사용하면 됩니다.

subject.send(completion: .finished)

Type 삭제

참고로 Type 삭제에 대해서만 간략히 알아보고 마무리합시다.

subscrbierpublisher에 대한 세부정보에 접근하지 않으면서 이벤트를 구독 하려는 상황에서 우리는 어떻게 처리해야 할까요?

let subject = PassthroughSubject<Int, Never>()
let publisher = subject.eraseToAnyPublisher()

바로 eraseToAnyPublisher() 통해 subjectAnyPublisher<Int, Never> 으로 만들어줬습니다. 해당 publisherPassthroughSubject 였다는 사실을 숨기는 것이죠!

AnyPublishersend(_:) 가 없기 때문에 publisher에 값을 추가할 수는 없답니다.

마무리

이렇게 Subject를 마지막으로 지금까지 PublisherSubscriber에 대한 개념들을 쭉 훑어보았는데요! 모쪼록 도움이 되었으면 좋겠습니다!

다음 포스팅에서는 publisher에서 받은 값들을 변형, 결합, 필터링하는 방법에 대해서! 즉 Operator를 주제로 돌아올게요! !

그럼 안녕!!!!!!!!!!!!

이전 포스팅 👈🏻

다음 포스팅 👉🏻

참고

--

--

Responses (1)