Combine에는 Publisher와 Subscriber가 있습니다.
이름만 봐도 둘이 무언갈 주고받으면서 사이좋게 지내겠구나...가 느껴지지않나요?ㅎㅎ
이 친구 둘이서 이벤트를 주고 받고 하는 사이인데요.
Combine을 배우는 이유가 이벤트를 어떻게 처리하는지 알기 위해서잖아요.
그걸 알기 위해서 이 Publisher와 Subscriber가 무엇인지 살펴보도록 하겠습니다.
일단 퍼블리셔와 섭스크라이버가 어떤 친구들인지 먼저 알아야겠죠?
하지만 이름만 봐도 감이 오죠?
Publisher: 이벤트를 어떻게 발행할 것인지 정의한 프로토콜
Subscriber: 퍼블리셔에게 이벤트를 요청하고, 퍼블리셔가 여러 체인을 통해 전달해준 이벤트를 받아 처리하는 걸 정의한 프로토콜
이 두개는 프로토콜인데 일단 퍼블리셔는 주는 친구고 섭스크라이버는 요청하고 받는 친구다! 라고 이해하고 넘어가보도록 할게요.
Publisher
Publisher 프로토콜을 따르는 모든 Publisher는 필수로 3가지 요소를 정의해야 합니다.
1. `Output`: 퍼블리셔가 내보내는 값의 타입입니다.
2. `Failure`: Error 프로토콜을 채택한 퍼블리셔가 내보내는 값의 타입입니다.
3. `subscribe(_:)`: 퍼블리셔에게 파라미터로 Subscriber를 전달합니다. Subscriber와 Publisher의 값과 에러가 일치해야 합니다.
퍼블리셔가 실제로 값을 생산해내는 건 아니기 때문에 값타입인 struct입니다.
Subscriber 프로토콜을 따르는 모든 Subscriber는 2개의 associatedtype과 3개의 메소드를 구현해야 합니다.
1. `Input`: Publisher를 통해서 제공받을 값의 타입입니다.
2. `Failure`: Publisher를 통해서 제공받을 에러의 타입입니다. Error 프로토콜을 따릅니다.
3. `receive(subscription:)`: Subscriber가 Publisher를 구독(퍼블리셔의 subscribe메서드 호출)한 후 이 메소드를 퍼블리셔가 호출합니다. 그러면 데이터의 흐름을 제어할 수 있는 subscription을 전달받습니다. 이 subscription 인스턴스는 퍼블리셔에게 요청하고, 구독을 취소하는 데에 사용됩니다.
4. `receive(_:)`: 퍼블리셔(정확히는 subscription)가 값을 만들어서 subscriber에게 전달할 때 사용합니다. 반환값은 subscriber가 받을 요소가 몇 개(Subscribers.Demand)인지를 알려줍니다.
5. `receive(completion:)`: 퍼블리셔가 정상적으로 혹은 에러로 인해 종료되었음을 통보합니다.
섭스크라이버는 값을 수신받으면서 상태를 변화하기 때문에 참조타입인 class 입니다.
extension Subscribers {
final public class Sink<Input, Failure> : Subscriber, Cancellable, CustomStringConvertible, CustomReflectable, CustomPlaygroundDisplayConvertible where Failure : Error {
/// 값을 받을 때 실행할 클로저
final public var receiveValue: (Input) -> Void { get }
/// completion을 실행할 때의 클로저
final public var receiveCompletion: (Subscribers.Completion<Failure>) -> Void { get }
/// sink의 초기화 구문, 파라미터에 클로저를 받는다.
public init(receiveCompletion: @escaping ((Subscribers.Completion<Failure>) -> Void), receiveValue: @escaping ((Input) -> Void))
/// 퍼블리셔가 섭스크라이버의 이 메서드를 사용해서 성공적으로 퍼블리셔와 연결되었음을 알린다.
/// 전달받은 `Subscription`을 사용해서 퍼블리셔에게 값을 요청한다.
final public func receive(subscription: Subscription)
/// 퍼블리셔가 값을 섭스크라이버에게 전달할 때 사용한다.
/// - Input: 전달받은 값
/// - Returns A `Subscribers.Demand`: 섭스크라이버 몇 개의 값을 떠 받게 될 지를 알려줌
final public func receive(_ value: Input) -> Subscribers.Demand
/// 퍼블리셔가 정상적으로 혹은 오류로 종료되었음을 알린다.
final public func receive(completion: Subscribers.Completion<Failure>)
/// Cancel
final public func cancel()
}
}
섭스크라이버의 한 종류인 Sink의 코드에 정리가 잘 되어 있어서 해석해서 가져왔습니다.
위의 Publisher와 Subscriber가 서로의 메서드를 호출하면서 어떻게 `fit together`하는지 정리한 그림이 아래와 같습니다.
1. 섭스크라이버는 퍼블리셔의 `subscribe` 메서드를 사용해 구독합니다.
2. 퍼블리셔는 섭스크라이버의 `receive(subscription:)`을 호출해 `subscription`을 보냅니다.
3. 섭스크라이버는 `subscription`을 사용해서 N개의, 혹은 무한한 개수의 요청을 보냅니다.
4. 퍼블리셔는 섭스크라이버에게 N개 이하의 value를 보냅니다.
5. 퍼블리셔가 유한한 경우 완료 혹은 오류를 보냅니다.
즉, 하나의 `subscription`은 0개 혹은 여러개의 value와 하나의 completion을 의미합니다.
이런 퍼블리셔와 섭스크라이버를 직접 구현하지 않아도 애플에서 제공하는 것으로 간단하게 만들 수 있는데,
아래와 같습니다.
Publisher 만들기
1. Just: 하나의 값을 즉시(동기) 발생시킬 때 사용
2. Future: 하나의 값을 비동기적으로 발생시키고자할 때 사용
3. Deferred: 구독이 일어날 때 실제 사용될 Publisher가 결정됨
4. Empty: 아무 값을 발생시키지 않는 Publisher (즉시 종료 or 영원히 종료안되도록 설정 가능)
5. Record: 구독할 때마다 이전에 발생했던 값들을 다시 내보냄
+ 여러 동기/비동기 인터페이스에 publisher 프로퍼티 제공 (ex: NotificationCenter)
+ Subject(외부에서 값을 주입할 수 있는 Publisher) `send(_:)` 메소드를 통해 값이나 완료, 에러를 보낼 수 있음
- `PassthroughSubject` : 마지막에 내보낸 값 저장 안함
- `CurrentValueSubject` : 마지막에 내보낸 값 저장
Subscriber 만들기
1. AnySubscriber의 생성자는 구현 필수 메소드를 클로저로 받습니다.
2. Sink, Assign이라는 기본 Subscriber를 제공합니다.
3. 직접 구현을 해야한다면 Publisher가 Subscription 객체를 가지도록 하고, Cancellable 프로토콜을 채택하는게 좋습니다. Publisher가 Subscription에 대한 참조를 가질 때 순환 참조를 고려해야 합니다.
출처
https://developer.apple.com/videos/play/wwdc2019/722/?time=658
https://developer.apple.com/documentation/combine/receiving-and-handling-events-with-combine
https://developer.apple.com/documentation/combine/publisher
https://developer.apple.com/documentation/combine/subscriber
https://developer.apple.com/documentation/combine/processing-published-elements-with-subscribers
'Combine' 카테고리의 다른 글
Combine | Scheduler (0) | 2022.06.10 |
---|---|
Combine | Operator (0) | 2022.06.02 |
Combine | Overview (0) | 2022.05.09 |