반응형
반응형

Publisher

#Combine #Publisher

Just

  • 가장 단순한 형태의 Publisher 입니다. 에러 타입은 항상 Never 입니다.
    Just((0...5))
      .sink { value in
          print(value) // 0...5
      }

 

Future

  • 일반적으로 Publisher의 처리를 sink 라는 구독을 형태로 많이 처리하게 되는데 이 때 클로저를 전달하는 과정에서 콜백 기반의 completion 핸들러를 사용하게 되는데 Futrue를 통하여 더욱 깔끔한 코드 작성이 가능해 집니다.
  let myFuture = Future<Int, Never> { promise in
      promise(.success(10))
  }
  myFuture.sink { value in
      print(value) // 10
  }
  • URLSeesion 이나 Alamofire 등 RestFul 관련 API 비동기 요청시에 해당 요청이 성공했는지, 실패 했는지에 대한 여부를 리턴해주는 예제 입니다.
func isSuccessAPIRequest() -> AnyPublisher<Bool, Never> { 
    Future<Bool, Never> { promise in 
        urlRequestPublisher
            .sink(
                receiveCompletion: { completion in 
                    switch completion {
                    case .finished: 
                        print("finished")
                        promise(.success(true))
                    case .failure(let error): print(error.localizedDescription)
                        promise(.success(false))
                    }
                }, receiveValue: { value in
                    print(value)
                }
            )
    }
    .eraseToAnyPublisher()
}

isSuccessAPIRequest()
	.sink { 
		if $0 {
			// true
		} else {
			// false
		}
	}

 

Fail

  • 지정한 종류의 에러로 즉시 종료하는 Publisher 입니다.

 

Empty

  • 값을 게시하지 않고 선택적으로 즉시 완료되는 Publisher 입니다.
  Empty<String, Never>()
      .sink(
          receiveCompletion: { 
              print($0) // finished
          },
          receiveValue: {
              print("receiveValue : \($0)") // 출력 안함
          }
      )

 

Deferred

  • 구독이 일어나기 전까지 대기상태로 있다가 구독이 일어 났을 때 Publisher가 결정이 됩니다. 클로저 안에는 지연 실행 할 Publisher 를 반환합니다.
    Deferred { Just(Void()) }
      .sink(receiveValue: { print("Diferred") })

 

Sequence

  • 요소의 주어진 Sequence를 반환하는 Publisher 입니다.
  • Publisher가 Sequence에 있는 요소들을 하나 하나 제공해주며 모든 요소들이 다 제공되었을 때 종료됩니다.
    Publishers.Sequence<[Int], Never>(sequence: [1, 2, 3])
        .sink(receiveValue: { 
            print("Sequence : \($0)")
        })
    /*
     Sequence : 1
     Sequence : 2
     Sequence : 3
     */
반응형

Back pressure

Combine은 구독자가 데이터의 흐름을 제어하도록 설계되어 있습니다. 그래서 구독자는 파이프라인에서 처리가 발생하는 시기와 대상도 제어합니다. 구독자가 얼마나 많은 정보를 원하거나 수용할 수 있는지에 대한 정보를 제공함으로써 파이프라인 내에서 처리를 주도한다는 것을 의미합니다. 이러한 요청은 구성된 파이프라인을 통해 전파되며 각 Operator는 차례대로 데이터 요청을 수락하고 연결된 Publisher에게 정보를 요청합니다.
이 것을 백 프레셔 라고 불리는 Combine의 기능이라고 합니다!

파이프라인이 취소되면 해당 파이프라인은 다시 시작되지 않습니다. 취소된 파이프라인 대신 새로운 파이프라인을 만들어야 합니다.

생명주기(Life Cycle)

lifeCycle

1. 구독자가 게시자에게 연결되면 구독자의 호출로 시작됩니다. .subscribe(_: Subscriber

2. 게시자는 차례대로 구독을 호출을 확인합니다. receive:subscription: Subscription

3. 구독이 승인된 후 구독자는 값을 요청합니다 request(_: Demand)

4. 게시자가 값이 있다면 값을 보내게 됩니다. 게시자는 요청 한 양보다 더 많은 값을 줄 수 없습니다. receive(_: Input)

5. 구독이 완료된 후 언제든지 구독자는 다음과 같이 구독을 취소할 수 있습니다. .cancel()

6. 게시자는 선택적으로 완료가 되었다는것을 보냅니다. 정상적인 종료일 수도 있고 오류 유형을 알려주는 완료일 수도 있습니다. 취소된 파이프라인은 완료를 보내지 않습니다. receive(completion:)

반응형

Publisher(게시자)

  • 요청 시 데이터를 제공해줌
  • 구독이 없는 경우 Publisher는 데이터를 제공하지 않음
  • 두 가지 유형을 제공함(Outpt Type, Failure Type)

publisher

Subscriber(구독자)

  • 구독자는 데이터를 요청하고 Publisher가 제공한 데이터(및 오류)를 처리해야 함
  • 입력에 대한 유형과 실패에 대한 유형 두 가지 연관된 유형
  • 구독자는 데이터를 요청을 시작하고 수신하는 데이터의 양을 제어함
  • Subscriber가 없으면 게시가 아예 되지 않기 때문에 Subscriber는 작업을 주도하는 것으로 생각할 수 있습니다.
    subscriber

Operator(연산자)

  • 연산자는 Publisher Protocol과 Subscriber Protocol을 모두 채택하는 클래스
  • 게시자 구독 및 모든 구독자에게 결과 보내기를 지원
  • 게시자가 데이터를 제공하고 구독자가 요청한 데이터를 처리, 반응 및 변환하기 위해 체인을 만들 수 있음
    operator

연산자는 값 또는 유형을 변환하는데 사용되며 스트림을 분할 또는 복제하거나 스트림을 병합할 수도 있습니다. 그리고 항상 (출력/실패) 유형의 조합으로 정렬되어야 합니다. 만약 유형이 올바르지 않으면 컴파일러에서 오류가 발생합니다!


아래는 간단한 Combine 파이프라인 입니다.

let _ = Just(5)                     // 1
    .map { value -> String in     // 2
        return "a string"
    }
    .sink { receivedValue in      // 3
        print("The end result was \(receivedValue)")
    }

1) Just 파이프라인은 정의되어있는 값으로 응답하는 게시자입니다. 이미 정의되어있는 값이기 때문에 실패 유형이 없죠? 그래서 Error 유형은 NEVER가 됩니다.
2) map을 통해 기존의 값을 기반으로 한 새로운 유형의 값을 반환할 수 있습니다. 예제에서는 기존 값을 무시하고 새로운 String 값을 반환합니다.
3) sink 구독자로 인해 파이프라인이 종료됩니다.

파이프라인은 출력 유형과 실패 유형 두 가지에 의해 연결된 일련의 작업입니다. 파이프라인을 생성할 때 최종 목표를 달성하기 위해 데이터, 유형(스트림의 성공 여부 등)을 변환하는 데 연산자를 사용하게 됩니다. 여기서 말하는 최종 목표는 대부분 사용자 인터페이스 요소를 변화시키는 작업이겠죠? 그렇기에 이러한 변환을 돕기 위한 게 바로 Combine의 연산자입니다.

Combine의 대표 연산자 tryMap

Combine의 연산자를 보다 보면 try###라는 이름의 연산자들이 있습니다. 모두 기존의 함수와 비슷한 동작을 하지만 실패 유형을 가지고 있어 실패 유형에 대한 처리를 도와줍니다. map은 위의 예제에서처럼 String만을 반환하지만 tryMap의 경우 유형을 제공하는 게시자를 <String, Never>을 사용하는 구독자로 끝납니다. 간단하게 throw를 해야 한다면 try가 붙은 연산자를 사용하면 됩니다.

let _ = Just(5) 
    .map { value -> String in 
        switch value {
        case _ where value < 1:
            return "none"
        case _ where value == 1:
            return "one"
        case _ where value == 2:
            return "couple"
        case _ where value == 3:
            return "few"
        case _ where value > 8:
            return "many"
        default:
            return "some"
        }
    }
    .sink { receivedValue in 
        print("The end result was \(receivedValue)")
    }
  • Just<Int, Never> 유형의 조합을 만들었고 정의된 단일 값을 제공합니다.
  • .map() 함수를 통해 Int를 받아 String으로 반환합니다. 실패 유형인 Never는 변경되지 않으므로 통과됩니다.
  • sink를 통해 <String, Never> 조합을 수신합니다.
반응형

+ Recent posts