Combine 공식 문서 파헤치기(2)

DongHeon·2023년 11월 8일
0

Combine

목록 보기
3/5
post-thumbnail

안녕하세요!!

오늘도 지난시간에 이어서 Combine 공식 문서를 정리하는 시간을 가져보도록 하겠습니다.

Controlling Publishing with Connectable Publishers

Connectable Publisher를 이용해 Publishing을 제어하는 방법에 대해 먼저 다뤄보겠습니다.

공식 문서에서는 한가지 예시를 보여주고 있습니다.

Subscriber1이 URL 데이터 가져오기를 요청합니다. 이 과정에서 Subscriber2를 중간에 동일한 Publisher에 연결합니다. 하지만 이미 데이터를 가져오는 작업이 종료되고 Subscriber2가 연결되었다면 데이터는 받지 못하고 완료했다는 알림만 받을 수 있습니다.

그럼 어떻게 해야 Subscriber2도 데이터를 받을 수 있을까요?
당연히 Subscriber2가 연결되기 전까지 데이터를 요청하지 않으면 됩니다.

ConnectablePublisher?

번역하면 게시를 연결하고 취소하기 위해 명시적인 수단을 제공하는 프로토콜입니다.

언제 사용해야 할까요? 공식 문서에서는 요소를 제작하기 전에 추가 구성이나 설정을 해야 하는 경우 사용한다고 나와있습니다.

그럼 위에서 발생한 문제를 해결해 보도록 하겠습니다.

그림을 보면 여전히 URL 데이터를 요청하는 DataTaskPublisherConnectablePublisher보다 앞에 있습니다. 하지만 connect() 함수가 호출되기 이전에는 데이터를 요청하지 않기 때문에 모든 Subscriber를 연결한 뒤 함수를 호출하면 됩니다.

설명한 방식을 사용한다면 race condition을 제거하고 데이터를 받을 수 있습니다.

race condition이란 두 개 이상의 프로세스가 공통 자원을 병행적으로(concurrently) 읽거나 쓰는 동작을 할 때, 공용 데이터에 대한 접근이 어떤 순서에 따라 이루어졌는지에 따라 그 실행 결과가 같지 않고 달라지는 상황을 말한다.

let url = URL(string: "https://example.com/")!
let connectable = URLSession.shared
    .dataTaskPublisher(for: url)
    .map() { $0.data }
    .catch() { _ in Just(Data() )}
    .share()
    .makeConnectable()


cancellable1 = connectable
    .sink(receiveCompletion: { print("Received completion 1: \($0).") },
          receiveValue: { print("Received data 1: \($0.count) bytes.") })


DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
    self.cancellable2 = connectable
        .sink(receiveCompletion: { print("Received completion 2: \($0).") },
              receiveValue: { print("Received data 2: \($0.count) bytes.") })
}


DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
    self.connection = connectable.connect()
}

ConnectablePublisher를 사용하기 위해서는 makeConnectable() 함수를 이용해 인스턴스를 래핑해야 합니다. 코드를 보면 1초 간격으로 구독자를 만들고 connect() 함수를 호출하는 것을 확인할 수 있습니다.

connect()는 유지해야 하는 Cancaelable인스턴스를 반환하기 때문에 원할 때 cancel()을 이용해 초기화를 취소할 수 있습니다

하지만 모든 경우에서 여러 구독자를 연결할 필요는 없습니다. 그럴 경우에는 autoconnect()를 이용해야 합니다. autoconnect()는 연결 되는 즉시 호출되어 구독자는 element를 받을 수 있습니다.

let cancellable = Timer.publish(every: 1, on: .main, in: .default)
    .autoconnect()
    .sink() { date in
        print ("Date now: \(date)")
     }

0개의 댓글