[Combine] Subject
Subject
는 publisher
의 일종입니다. 보다시피 Publisher
protocol을 착실하게 따르고 있쥬?
protocol Subject : AnyObject, Publisher
차이점은 지금까지와 달리 밖에서 값을 방출할 수 있다는 점입니다.
조금 더 자세히 알아 볼까요?
send(_:)를 통해 stream에 값을 주입할 수 있는 publisher
우리는 지금까지 아래 그림과 같은 예제만 봤어요. 외부에서는 값을 던질 수 없고 Publisher
내부에서 생성되어 나오는 값들만 받아서 처리했죠!
하지만 publisher
가 아닌 subject
를 사용하면 send(_:)
를 통해 외부에서도 저 1,3,5
로 이어지는 일련의 스트림에 값을 주입할 수 있다는 것입니다!!! 앗 물론 send(_:)
는 Subject
protocol에 정의되어 있답니다.
이것은 존재하는 명령형 코드를 Combine
모델에 적용하는데 유용하다…라고 하는데 여기까지는 무슨 뜻인지 저도 잘 모르겠네요ㅎ
여하튼 Subject
의 종류는 크게 PassthroughSubject
와 CurrentValueSubject
로 나뉘는데요! 이것들을 살펴보면서 Subject
에 대한 구체적인 개념을 잡아봅시다!!!!!!!!
PassthroughSubject
downstream
subscriber
에게element
를broadcast
하는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
에게element
를 broadcast
하는 subject
라는 문장이 이해가 되시나요?!
CurrentValueSubject
PassthroughSubject
와 달리 초기값
과 최근 발행된 element
에 대한 buffer
를 갖습니다! 예시를 통해 확인해보아요.
여기서 CurrentValueSubject
는 0
이라는 초기값과 함께 생성되었어요! 따라서 콘솔 창에는 아래와 같이 찍힙니다.
——— 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)
이렇게 발행된 값은 현재 값으로도 설정됩니다. 현재 값은 subject
의 value
값으로 확인할 수 있는데요! 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 삭제에 대해서만 간략히 알아보고 마무리합시다.
subscrbier
가 publisher
에 대한 세부정보에 접근하지 않으면서 이벤트를 구독 하려는 상황에서 우리는 어떻게 처리해야 할까요?
let subject = PassthroughSubject<Int, Never>()
let publisher = subject.eraseToAnyPublisher()
바로 eraseToAnyPublisher()
통해 subject
를 AnyPublisher<Int, Never>
으로 만들어줬습니다. 해당 publisher
가 PassthroughSubject
였다는 사실을 숨기는 것이죠!
AnyPublisher
는 send(_:)
가 없기 때문에 publisher
에 값을 추가할 수는 없답니다.
마무리
이렇게 Subject
를 마지막으로 지금까지 Publisher
와 Subscriber
에 대한 개념들을 쭉 훑어보았는데요! 모쪼록 도움이 되었으면 좋겠습니다!
다음 포스팅에서는 publisher
에서 받은 값들을 변형, 결합, 필터링하는 방법에 대해서! 즉 Operator
를 주제로 돌아올게요! !
그럼 안녕!!!!!!!!!!!!