안녕하세용가리 🐲
몇 주만의 글이지... 긁적..
긴말 말고 휘뚜루마뚜루 바로 들어가보자고 ~
Subject
외부 호출자가 값을 게시할 수 있는 메서드를 표시하는 Publisher
Subject | Apple Developer Documentation
A publisher that exposes a method for outside callers to publish elements.
developer.apple.com
Publisher와 Subscriber의 특징을 모두 가지고 있다. stream에 send(_:) 메서드를 호출해서 값을 주입할 수 있는 Publisher이다.
그래서 기존에 Combine을 사용하지 않던 코드에 Combine 모델을 적용하고 싶을 때 사용하면 좋다고 한다 ~
다시 정리해보자면, Subject는 Publisher의 일종으로 외부에서 send(_:) 메서드를 통해 일련의 stream에 값을 주입할 수 있다는 것 ✨
(지금까지 배운 Publisher 개념으로는 외부에서는 값을 주입할 수 없고, Publisher 내부에서 생성되어 나오는 값들만 받아서 처리 가능.)
이해를 돕기 위해 예시 코드를 살펴보자 !
(이누님의 블로그에서 예시 코드를 가져왔다. 너무 많은 도움이 되었음 ㅠ ㅠ)
enum MyError: Error {
case subscriberError
}
class StringSubscriber: Subscriber {
func receive(subscription: Subscription) {
subscription.request(.max(2))
}
func receive(_ input: String) -> Subscribers.Demand {
print(input)
return .max(1)
}
func receive(completion: Subscribers.Completion<MyError>) {
print("Completion")
}
typealias Input = String
typealias Failure = MyError
}
먼저 Subscriber를 만들어주었다. 이 Subscriber는 Input으로 String 타입을 받고, Error는 직접 만든 MyError를 사용한다. 초기 데이터 제한은 2로 설정하고 새로 값을 받을 때마다 1씩 증가되도록 설정하였다.
Subject는 프로토콜이기 때문에 직접 생성할 수도 있지만, 기존에 프레임워크 자체에 만들어진 Subject class가 존재한다. 2가지가 있는데 각 특징에 대해서는 아래에서 구체적으로 살펴보겠다 !
예시에서는 PassthroughSubject라는 것을 사용하였다. 생성 시에 바로 Input과 Error를 설정할 수 있는데, StringSubscriber랑 똑같이 설정하였음.
let subject = PassthroughSubject<String, MyError>()
subject 객체를 생성했다. subject도 일종의 Publisher이므로 여기에 Subscriber를 구독시킬 수 있다.
let subscriber = StringSubscriber()
subject.subscribe(subscriber)
이제 send(_:) 메서드 실행 !!
subject.send("A")
Subscriber가 정상적으로 값을 받아서 “A”가 출력되었다.
subject도 Publisher이기 때문에 sink를 통해 Subscriber를 생성할 수 있다. Subscriber를 하나 더 생성하고 값들을 보내보자.
let subscription = subject.sink(receiveCompletion: { (completion) in
print("Received Completion from sink \\(completion)")
}) { value in
print("Received Value from sink \\(value)")
}
subject.send("A")
subject.send("B")
subject.send("C")
subject.send("D")
value 뿐 아니라 completion도 보낼 수 있다. 단, completion을 보내고 나면 이후에는 추가적으로 value를 전송할 수 없다 !!!
subject.send(completion: .finished)
🤔 Subscriber 에서 값을 받는 순서는 출력할 때마다 바뀌었음 !! (sink로부터 값을 받았다는 메시지가 먼저 뜨는 경우가 있음.)
PassthroughSubject, CurrentValueSubject
둘의 차이점은 초기값 필요여부와 버퍼의 존재여부이다.
📍 PassthroughSubject
- 초기값 필요 X
- 가장 최근에 발행된 값에 대한 버퍼가 존재하지 않는다.
- Subscriber가 존재하지 않거나 현재의 Demand가 0이라면 value 값을 버린다.
📍 CurrentValueSubject
- 초기값 필요 O
- 가장 최근에 발행된 값에 대한 버퍼가 존재한다.
- value 파라미터를 통해 현재의 값을 확인할 수 있다. ( 해당 value에 값을 직접 할당할 수도 있다. )
출처 : https://inuplace.tistory.com/1024
위에서 등장한 함수와 타입에 대해 알아보자.
sink(_:), AnyCancellable, store(_:), send(_:)
sink(receiveValue:)
절대 실패하지 않는 Publisher에게 Closure-based 행동을 가진 Subscriber를 부착한다.
Apple Developer Documentation
developer.apple.com
func sink(receiveValue: @escaping ((Self.Output) -> Void)) -> AnyCancellable
let integers = (0...3)
integers.publisher
.sink { print("Received \\($0)") }
// Prints:
// Received 0
// Received 1
// Received 2
// Received 3
이 방법은 Subscriber를 만들고 Subscriber를 반환하기 전에 즉시 무제한의 값을 요청한다.
반환 값은 유지되어야 하며, 그렇지 않으면 스트림이 취소될 것이다. sink를 수행하면 AnyCancellable 타입의 데이터가 반환된다.
sink(receiveCompletion:receiveValue:)도 있다. 이 메서드는 Closure-based 동작으로 Subscriber를 연결한다.
인자에 따라 sink 메서드가 조금씩 달랐다. 그래도 하는 일은 Closure-based 동작으로 Subscriber를 연결하는 것으로 비슷했다.
AnyCancellable
AnyCancellable 타입은 구독에 대한 취소(Cancel)가 가능한 객체이다.
Apple Developer Documentation
developer.apple.com
취소될 때 정의한 클로저 블록을 실행해주는, 즉 취소를 해주는 객체
Subscriber 구현은 이 유형을 사용하여 호출자가 게시자를 취소할 수 있지만, Subscription 개체를 사용하여 항목을 요청할 수는 없도록 하는 "Cancellation token"을 제공할 수 있다.
Cancel만 가능하고 Request는 수행할 수 없다. 초기화가 해제되면 AnyCancellable 인스턴스가 자동으로 cancel()을 호출한다.
subsription.cancel()
취소하지 않으면 계속해서 Publisher로부터 발생하는 이벤트를 수신하며 메모리를 차지하게 된다. 그러므로 위 코드를 사용하여 취소하면 된다. 이때 cancel 메서드를 호출하지 않아도 만약 Subscriber 객체가 Scope를 벗어나면 자동으로 구독이 취소된다는 것을 유의해야 한다. (따라서 Subscriber 객체는 생성하는 위치도 중요하다 !)
cancel() 호출 시 다운스트림 구독 호출을 중지하도록 해준다. 호출 즉시 바로 취소 적용이 된다. 즉, cancel() 메서드가 호출되고나면 후속 호출에서 어떠한 작업도 진행되지 않는다.
store(in:)
Stores this type-erasing cancellable instance in the specified set. (Set에 취소 가능한 인스터스를 저장할 집합)
Apple Developer Documentation
developer.apple.com
sink를 통해 AnyCancellable로 반환된 걸 해당 메서드를 체이닝하여 하나의 집합에 담아준다.
store 메서드를 통해 cancellable로 여러 서브젝트를 취소를 담아주고 한번에 해제할 수 있도록 구현할 수 있다.
그리고 Subscription 객체를 사용해서 취소될 시, 해줄 역할을 정의해줄 수도 있고 사용할 수 있다.
출처 : https://green1229.tistory.com/226
send(completion:)
Sends a completion signal to the subscriber.
send(completion:) | Apple Developer Documentation
Sends a completion signal to the subscriber.
developer.apple.com
func send(completion: Subscribers.Completion<Self.Failure>)
Subject에서 젤 중요한 건,, 아무래도 send 메서드인 것 같다.
원래 이번 글은 Firebase와 Combine을 주제로 쓰려고 했는데,,
Subject는 send 메서드를 통해 "외부에서" 이벤트를 방출할 수 있는 특이한 Publisher
라 생각하여 기록해둘 필요성을 느껴 따로 정리를 해보았다.!!
'iOS > Combine' 카테고리의 다른 글
Combine (2) - Publisher, Subscriber, Subscription (0) | 2023.02.12 |
---|---|
Combine (1) - 개요 (1) | 2023.01.28 |