이번 글에서는 Publisher, Subscriber, Subscription의 개념과 간단한 사용 예시를 보면서
코드에서 사용되는 함수들을 정리할 것 !!!
Publisher, Subscriber
말 그대로 게시자와 구독자로, Publisher를 구독하는 객체 Subscriber가 Publisher가 발행하는 데이터 스트림을 받아서 처리할 수 있는 것이다.
Publisher Protocol
시간이 지남에 따라 값을 전달할 수 있는 타입을 명시하는 프로토콜
Apple Developer Documentation
developer.apple.com
class IntPublisher: Publisher {
typealias Output = Int
typealias Failure = Never
func receive<S>(subscriber: S) where S : Subscriber, Failure == S.Failure, Output == S.Input {
}
}
- Output은 어떤 타입의 데이터 스트림을 보낼 수 있는지를 나타내고, Failure는 실패할 경우 어떤 타입의 형태를 보내는지 나타낸다. (예시에서는 임의로 Int와 Never)
- 해당 Publisher를 구독하는 Subscriber는 Input과 Failure가 해당 Publisher의 Output, Failure와 일치해야 한다.
- receive<S>(subscriber: S) : 해당 Publisher를 구독하는 subscriber를 등록할 때 호출되는 메서드
Subscriber Protocol
Publisher로부터 값을 받을 수 있는 타입을 명시하는 프로토콜
Apple Developer Documentation
developer.apple.com
class IntSubscriber: Subscriber {
typealias Input = Int
typealias Failure = Never
func receive(subscription: Subscription) {
}
func receive(_ input: Int) -> Subscribers.Demand {
return .unlimited
}
func receive(completion: Subscribers.Completion<Never>) {
}
}
- Input은 어떤 타입의 데이터 스트림을 받을 수 있는지를 나타내고, Failure는 실패할 경우에 어떤 타입의 형태로 처리되는지를 나타낸다. (예시에서는 임의로 Int와 Never)
- 특정 Publisher를 구독하는 Subscriber는 Input과 Failure가 해당 Publisher의 Output, Failure와 일치해야 한다.
즉, 타입을 같게 해줘야 한다. (Subscriber의 Input = Publisher의 Output) - receive(subscription: Subscription) : 성공적으로 특정 Publisher에 대한 구독이 성공했을 때, Publisher로부터 Subscription이라는 구독을 대변하는 객체를 전달받는다. 이때 호출되는 메서드이다. 여기서 Subscription 객체를 활용해 Publisher로부터 얼마나 값을 전달 받을지 등을 결정할 수 있다.
- receive(_ input: Input) : 구독 중인 Publisher로부터 value를 전달 받았을 때 호출되는 메서드이다. 여기서 Subscribers.Demand 객체를 리턴해야 하는데, 이를 통해 Publisher로부터 얼마나 값을 전달받을지를 변경할 수 있다.
- receive(completion: Subscribers.Completion) : 구독 중인 Publisher로부터 completion을 전달 받았을 때 호출되는 메서드이다.
Subscription Protocol
Publisher와 Subscriber를 이어주는 프로토콜
Apple Developer Documentation
developer.apple.com
class TestSubscription: Subscription {
func request(_ demand: Subscribers.Demand) {
}
func cancel() {
}
}
- request : 여기에 Subscribers.Demand를 넘겨주게 되는데, 이를 통해 Subscriber가 얼마나 데이터를 전달받을지를 결정한다.
- cancel : 구독을 취소한다.
Subscribers.Demand
Subscriber와 Subscribers는 다르다. Subscriber는 프로토콜이고 Subscribers는 열거형으로 타입이 다르고, Subscribers는 Subscriber 역할을 하는 타입들을 정의해둔 곳이다. 그 안에는 Demand, Completion, Sink, Assign이 있다. 일단 위 코드에서 사용되는 Demand만 보고 넘어가겠다.
Subscriber 프로토콜에 존재하는 receive(_ input: )메서드는 Subscribers.Demand 타입을 리턴한다. 또, Subscription의 request에서도 Subscribers.Demand 타입을 파라미터로 받았다.
Subscribers.Demand는 Subscriber가 요청하는 아이템(데이터)의 개수이다. Subscriber는 이를 Subscription을 통해 Publisher에게 전달한다.
Subscribers.Demand는 종류가 3가지이다.
- max(Int) : 최대 개수를 설정한다.
- unlimited : 개수 제한이 없도록 설정한다.
- none : 아무것도 받지 않도록 설정한다.
아래에서 Subscriber 프로토콜을 통해 직접 만든 Subscriber class를 살펴보며 이를 이해해보자.
import Combine
class StringSubscriber: Subscriber {
func receive(subscription: Subscription) {
print("Received Subscription")
subscription.request(.max(3))
}
func receive(_ input: String) -> Subscribers.Demand {
print("Received Value", input)
return .none
}
func receive(completion: Subscribers.Completion<Never>) {
print("Completed")
}
typealias Input = String
typealias Failure = Never
}
StringSubscriber는 String 데이터를 받고 에러는 받지 않는(Never) Subscriber이다.
receive(subscription: Subscription)메서드에서 subscription 객체를 활용해 request 메서드를 수행한다. 이는 최초로 Subscriber.Demand를 전달해 Publisher로부터 데이터를 몇 개 받을지 설정한다. 위 코드에서는 .max(3)를 파라미터값으로 활용했기 때문에 3개로 제한되었다.
receive(_ input: String) 메서드에서는 값을 받을 때마다 추가적으로 제한을 늘릴 수 있다. (변경이 아니라 추가임! ) 위 코드에서는 .none을 리턴하고 있기 때문에 값을 전달 받아도 제한은 변경되지 않고 3개일 것이다.
let publisher = ["A","B","C","D","E","F","G","H","I","J","K"].publisher
let subscriber = StringSubscriber()
publisher.subscribe(subscriber)
이를 실행해보면 결과값이 3개만 나온다.
func receive(_ input: String) -> Subscribers.Demand {
print("Received Value", input)
return .unlimited
}
하지만 receive(_ input: String) 메서드에서 리턴값을 .none에서 .unlimited로 수정해주면 아래와 같이 출력된다.
전체값을 받고, Completion까지 전달받는다. receive(_ input: String) 메서드의 리턴값은 기존에 설정한 데이터 제한에서 그만큼 추가하겠다는 의미이므로 첫 Value를 받은 순간부터 데이터 개수의 제한이 없어진다.
따라서 전체값이 받아진다. (기존에는 .none이었기 때문에 최초로 설정한 제한 3개가 유지되면서 값이 3개만 받아졌다.)
Combine 객체들의 상호작용
https://www.donnywals.com/understanding-combines-publishers-and-subscribers/
- Subscriber 객체가 Publisher에 구독을 시작한다.
- Publisher가 Subscription 객체를 생성한다.
- Publisher가 이를 Subscriber 객체에게 전달한다.
- Subscriber가 Subscription에게 values를 요청한다.
- Subscription이 values를 가져온다.
- Subscription이 Subscriber에게 values를 전달한다.
- Subscription이 Subscriber에게 completion을 전달한다.
이렇게 Publisher, Subscriber, Subscription 각 중요 키위드들의 개념은 정리해보았다.
서버와 비동기 통신을 하려면 어떻게 활용해야 할까..
Firebase와 Combine을 같이 사용하는 방법을 다음 글에서 다뤄봐야겠다.!!!
이 글에서는 Combine에서 각 프로토콜의 전체적인 흐름을 알기 위해 작성해보았다.
( 까먹으면 보고 또 보려고 ,, ㅎㅅㅎ )
'iOS > Combine' 카테고리의 다른 글
Combine (3) - Subject (0) | 2023.04.02 |
---|---|
Combine (1) - 개요 (1) | 2023.01.28 |