티스토리 뷰

728x90

 

share()

값(value)으로 보다는 참조(reference)로 publisher를 공유할 수 있게 해줍니다.

즉, share() 메서드는 일반적으로 struct로 되어있는 Publisherreference타입으로 반환해줌!

subscriber는 구독후에 upstream publisher가 방출하는 값을 받게됩니다.
subscriber가 upstream publisher가 완료된 공유 publisher를 구독하면, 해당 subscriber는 완료 이벤트(completion event)만 받습니다.

import Combine
import Foundation

let shared = URLSession.shared
    .dataTaskPublisher(for: URL(string: "https://www.raywenderlich.com")!)
    .map(\.data)
    .print("shared")
    .share()

print("subscribing first")

let subscription1 = shared
    .sink(receiveCompletion: { _ in },
          receiveValue: { print("subscription1 received: '\($0)'") }
    )

print("subscribing second")

let subscription2 = shared
    .sink(receiveCompletion: { _ in },
          receiveValue: { print("subscription2 received: '\($0)'") }
    )

//subscribing first
//shared: receive subscription: (DataTaskPublisher)
//shared: request unlimited
//subscribing second
//shared: receive value: (266289 bytes)
//subscription1 received: '266289 bytes'
//subscription2 received: '266289 bytes'
//shared: receive finished​

• 첫번째 구독 subscription1이 DataTaskPublisher 를 실행시킵니다.
• 두번째 구독 subscription2에서 바뀐게 없이 publisher를 계속 실행. 두번째 요청은 하지않음.
• request가 끝나면, publisher가 두 구독자 (subscription1, subscription2)에게 데이터 결과값을 방출한후 완료됩니다.


한줄요약: share() 를 사용하면, subscriber가 여럿 있어도 요청을 한번만 보내고 결과값은 다같이 받습니다.

share()의 역할을 확인해보기 위해, 위 예제코드에 share() 메서드를 주석 처리해보고 결과값을 보면
//subscribing first
//shared: receive subscription: (DataTaskPublisher)
//shared: request unlimited
//subscribing second
//shared: receive subscription: (DataTaskPublisher)
//shared: request unlimited
//shared: receive value: (266289 bytes)
//subscription1 received: '266289 bytes'
//shared: receive finished
//shared: receive value: (266289 bytes)
//subscription2 received: '266289 bytes'
//shared: receive finished

request를 두번 받는걸 볼 수 있습니다.


NOTE
이미 완료된 shared publisher를 구독하면 completion 값만 받게 됩니다. (아래 코드 & 결과 참고)

var subscription2: AnyCancellable? = nil
    
// shared publisher 완료 후 5초 후에 구독 시도
DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
    print("subscribing second")
    
    subscription2 = shared
        .sink(
            receiveCompletion: { print("subscription2 completion \($0)") },
            receiveValue: { print("subscription2 received: '\($0)'") }
        )
}
subscription2에선 completion 값만 받아옴

 

 

 

multicast(_:)

multicast(_:)은 ConnectablePublisher를 반환합니다.
즉, connect()메서드를 호출하기 전까지 upstream publisher를 구독하지 않습니다.

예제코드
import Combine
import Foundation

// 1
let subject = PassthroughSubject<Data, URLError>()

// 2
let multicasted = URLSession.shared
    .dataTaskPublisher(for: URL(string: "https://www.raywenderlich.com")!)
    .map(\.data)
    .print("multicast")
    .multicast(subject: subject)

// 3
let subscription1 = multicasted
    .sink(receiveCompletion: { _ in },
          receiveValue: { print("subscription1 received: '\($0)'") }
    )

let subscription2 = multicasted
    .sink(receiveCompletion: { _ in },
          receiveValue: { print("subscription2 received: '\($0)'") }
    )

// 4
let cancellable = multicasted.connect()

//multicast: receive subscription: (DataTaskPublisher)
//multicast: request unlimited
//multicast: receive value: (266296 bytes)
//subscription2 received: '266296 bytes'
//subscription1 received: '266296 bytes'
//multicast: receive finished​

1. upstream publisher가 방출하는 값과 완료 이벤트를 주고받는 subject를 만듭니다.
2. 1번에서 만들었던 subject를 이용해 mulicasted publisher를 만듭니다.
3. multicasted publisher를 구독합니다.
4. upstream publisher에 연결합니다. (connect() 사용)  

(질문) GCD에 DispatchGroup에 notify() 같은느낌..?

NOTE
mulicast publisher도 ConnectablePublisher기 때문에 autoconnect()를 사용할 수 있습니다.


네트워킹 작업같은 경우 sharing subscription을 사용하자! 
why?
메모리 문제나 불필요한 네트워크 요청으로 서버를 과부하가 일어날 수 있기 때문.

 

subscription2에선 completion 값만 받아옴

 

future

클로저 내부 동작을 즉시 실행 합니다.

코드랑 결과를 보면 이해하기 
import Foundation
import Combine

// 1
func performSomeWork() throws -> Int {
  print("Performing some work and returning a result")
  return 5
}

// 2
let future = Future<Int, Error> { fulfill in
  do {
    let result = try performSomeWork()
    // 3
    fulfill(.success(result))
  } catch {
    // 4
    fulfill(.failure(error))
  }
}

print("Subscribing to future...")

// 5
let subscription1 = future
  .sink(
    receiveCompletion: { _ in print("subscription1 completed") },
    receiveValue: { print("subscription1 received: '\($0)'") }
  )

// 6
let subscription2 = future
  .sink(
    receiveCompletion: { _ in print("subscription2 completed") },
    receiveValue: { print("subscription2 received: '\($0)'") }
  )

//Performing some work and returning a result
//Subscribing to future...
//subscription1 received: '5'
//subscription1 completed
//subscription2 received: '5'
//subscription2 completed

 

728x90