개발
Combine Publisher - AnySubscriber
한번사는인생~키야
2024. 7. 8. 12:25
728x90
Combine에서 특정 구독자 유형 정보를 지우는 유형입니다. 이를 통해 퍼블리셔 로직 내에서 서로 다른 구체적인 유형의 구독자를 균일하게 처리할 수 있습니다.
다음은 세부 내용입니다.
- 유형 삭제: 실제 구독자 유형을 숨겨서 구체적인 세부 정보를 알지 못해도 다양한 구체적 구현의 구독자를 대상으로 작업할 수 있습니다. AnySubscriber
- 이익:
작동 방식 AnySubscriber:
- 게시자는 프로토콜을 준수하는 모든 구독자가 구독할 수 있는 AnyPublisher유형 을 반환합니다 .Subscriber
- 을 구독하면 AnyPublisher게시자 는 AnySubscriber인스턴스를 받습니다.
- Subscriber게시자는 프로토콜에 정의된 메서드(예: receive(_:)및 ) 를 통해 구독자와 상호 작용합니다 receive(completion:).
사용 AnySubscriber:
대부분의 경우 인스턴스를 직접 만들거나 조작하지 않습니다 AnySubscriber. 퍼블리셔는 내부적으로 유형 삭제를 처리합니다. 그러나 다음과 같은 몇 가지 방법이 발생할 수 있습니다 AnySubscriber.
- 사용자 정의 연산자: 구독자와 함께 작동하는 사용자 정의 연산자를 정의하는 경우 호환성을 처리해야 할 수도 있습니다 .AnySubscriber
- 주어 변환: 서로 다른 주어 유형(예 : ) 간에 변환하는 경우 백그라운드에서 작업이 필요할 수 있습니다. PassthroughSubjectCurrentValueSubject AnySubscriber
대안 AnySubscriber:
- 직접 구독자 유형: 특정 구독자 구현(예 : 또는 ) 을 사용하는 경우 대신 해당 유형을 직접 사용할 수 있습니다 .SinkAssignAnySubscriber
- 고차 연산자: Combine은 , , 와 같은 다양한 연산자를 제공하며 , 이를 통해 를 직접 처리하지 않고도 구독과 값 조작을 처리할 수 있습니다 .sink assigntryMapAnySubscriber
키 포인트:
- AnySubscriber일반적인 게시자 상호작용을 위해 특정 구독자 유형을 지웁니다.
- 이를 통해 게시자 구현이 간소화되고 코드 유지 관리가 용이해집니다.
- 대부분의 경우에는 . 을 직접 사용할 필요가 없습니다 AnySubscriber.
이해하면 AnySubscriberCombine 에서 퍼블리셔와 구독자 간의 유형 관계를 더 잘 파악하고 더 일반적이고 유연한 퍼블리셔 코드를 작성할 수 있습니다.
Combine에서 이 기능을 어떻게 사용하는지 또는 사용자 정의 연산자에서 어떻게 활용할 수 있는지에 대한 몇 가지 예가 있습니다
1. 정수 필터링을 위한 사용자 정의 연산자:
import Combine
struct FilterIntegersOperator<S: Subscriber>: Operator<Int, Int> where S: Subscriber, S.Input == Int {
let predicate: (Int) -> Bool
func makePublisher(upstream: AnyPublisher<Int, Error>) -> AnyPublisher<Int, Error> {
FilterIntegersPublisher(upstream: upstream, predicate: predicate)
}
class FilterIntegersPublisher: Publisher {
typealias Output = Int
typealias Failure = Error
let upstream: AnyPublisher<Int, Error>
let predicate: (Int) -> Bool
init(upstream: AnyPublisher<Int, Error>, predicate: @escaping (Int) -> Bool) {
self.upstream = upstream
self.predicate = predicate
}
func subscribe<S: Subscriber>(_ subscriber: S) -> Subscription {
let subscription = upstream.subscribe(AnySubscriber(makeSubscriber: { subscriber in
let downstreamSubscriber = FilterDownstreamSubscriber(
subscriber: subscriber,
predicate: predicate
)
return downstreamSubscriber
}))
return subscription
}
}
class FilterDownstreamSubscriber: Subscriber {
typealias Input = Int
typealias Failure = Error
let subscriber: S
let predicate: (Int) -> Bool
init(subscriber: S, predicate: @escaping (Int) -> Bool) {
self.subscriber = subscriber
self.predicate = predicate
}
func receive(_ input: Int) -> Subscribers.Demand {
if predicate(input) {
subscriber.receive(input)
return .requested(1)
} else {
return .none
}
}
func receive(completion: Subscribers.Completion) {
subscriber.receive(completion)
}
func receive(subscription: Subscription) {
subscriber.receive(subscription)
}
}
}
let upstream = Just([1, 2, 3, 4, 5])
let filteredPublisher = upstream
.pipe(through: FilterIntegersOperator(predicate: { $0 % 2 == 0 }))
.sink(receiveValue: { filteredValue in
print("Filtered value: \(filteredValue)")
})
설명:
- 우리는 게시자와 필터링 조건을 입력으로 FilterIntegersOperator받는 사용자 정의 연산자를 정의합니다 .Int
- 이 makePublisher메서드는 FilterIntegersPublisher인스턴스를 생성합니다.
- FilterIntegersPublisher업스트림 게시자를 구독하고 해당 구독을 .으로 래핑합니다 FilterDownstreamSubscriber.
- FilterDownstreamSubscriber수신된 값을 술어를 사용하여 필터링하고 일치하는 값만 다운스트림 구독자에게 전달합니다.
2. 과목 유형 변환:
import Combine
let passthroughSubject = PassthroughSubject<String, Never>()
let currentValueSubject = CurrentValueSubject<String, Never>("Initial Value")
// Converting PassthroughSubject to CurrentValueSubject
let convertedSubject = passthroughSubject
.prefix(1) // Take the first value
.connect() // Connect the publisher to start emitting
.subscribe(currentValueSubject) // Subscribe to the CurrentValueSubject
passthroughSubject.send("Hello") // PassthroughSubject emits a value
print(currentValueSubject.value) // CurrentValueSubject now holds the emitted value
설명:
- 우리는 a PassthroughSubject와 a를 생성합니다 CurrentValueSubject.
- PassthroughSubject를 a로 변환하려면 CurrentValueSubject첫 번째로 방출된 값만 가져온 다음 prefix(1)게시자를 연결하여 방출을 시작합니다.
- 변환된 게시자를 .에 구독합니다 CurrentValueSubject.
- 우리가 값을 보내면 PassthroughSubject, 의 CurrentValueSubject값은 방출된 값으로 업데이트됩니다.
키 포인트:
- AnySubscriberCombine에서 게시자와 구독자 간의 구독과 가치 전달을 처리하는 데 사용됩니다.
- 구독자와 작업할 때 사용자 정의 동작을 구현하기 위해 사용자 정의 연산자를 활용할 수 있습니다 .AnySubscriber
- 이해하면 AnySubscriberCombine의 퍼블리셔-구독자 모델 내의 관계 유형과 상호작용을 파악하는 데 도움이 됩니다.
기억하세요, 주로 Combine이 유형 삭제 및 게시자와 구독자 간의 일반적인 상호 작용을 처리하는 내부 메커니즘입니다. 대부분의 경우 인스턴스 와 직접 작업할 필요는 없습니다 . 그러나 그 역할을 이해하면 Combine에 대한 이해가 향상될 수 있습니다.