개발

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)")

  })

 

설명:

  1. 우리는 게시자와 필터링 조건을 입력으로 FilterIntegersOperator받는 사용자 정의 연산자를 정의합니다 .Int
  2. 이 makePublisher메서드는 FilterIntegersPublisher인스턴스를 생성합니다.
  3. FilterIntegersPublisher업스트림 게시자를 구독하고 해당 구독을 .으로 래핑합니다 FilterDownstreamSubscriber.
  4. 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

 

설명:

  1. 우리는 a PassthroughSubject와 a를 생성합니다 CurrentValueSubject.
  2. PassthroughSubject를 a로 변환하려면 CurrentValueSubject첫 번째로 방출된 값만 가져온 다음 prefix(1)게시자를 연결하여 방출을 시작합니다.
  3. 변환된 게시자를 .에 구독합니다 CurrentValueSubject.
  4. 우리가 값을 보내면 PassthroughSubject, 의 CurrentValueSubject값은 방출된 값으로 업데이트됩니다.

키 포인트:

  • AnySubscriberCombine에서 게시자와 구독자 간의 구독과 가치 전달을 처리하는 데 사용됩니다.
  • 구독자와 작업할 때 사용자 정의 동작을 구현하기 위해 사용자 정의 연산자를 활용할 수 있습니다 .AnySubscriber
  • 이해하면 AnySubscriberCombine의 퍼블리셔-구독자 모델 내의 관계 유형과 상호작용을 파악하는 데 도움이 됩니다.

기억하세요, 주로 Combine 유형 삭제 게시자와 구독자 간의 일반적인 상호 작용을 처리하는 내부 메커니즘입니다. 대부분의 경우 인스턴스 직접 작업할 필요는 없습니다 . 그러나 역할을 이해하면 Combine 대한 이해가 향상될 있습니다.