Combine

Combine - multicast / share

GREEN.1229 2022. 3. 24. 20:01

안녕하세요. 그린입니다🟢

이번 포스팅에서는 다시 컴바인 세계로 돌아와 multicast와 share에 대해 알아보겠습니다🙏🏻

 

우선 어떤걸 하는 친구들이냐!?
하나의 Publisher로 부터 구독을 한 여러 Subscriber들에게 각각 이벤트를 여러번 발행하는게 아니라
하나의 요소에 한번의 이벤트만 발생시켜줄 수 있도록 해주는 친구들입니다🙌

 

이번건 꽤나 간단히 이해될 수 있으니 한번 해보시죠!

multicast

공식문서를 보면 클로저를 적용해 구독자에게 요소를 전달하는 subject를 만들어준다고해요.

역시 공식문서의 정의는 어려워...🤯

우선 그러려니~ 하고 정의된 코드를 보시죠!

func multicast<S>(_ createSubject: @escaping () -> S) -> Publishers.Multicast<Self, S> where S : Subject, Self.Failure == S.Failure, Self.Output == S.Output

요렇게 되어있어요. 

아직까지 감이 안오시죠?

그럼 기존에는 어떻게 사용되었는지 또 multicast가 어떻게 사용될지 보시죠!

 

[multicast 미사용]

let pub = ["First", "Second", "Third"].publisher
    .map( { return ($0, Int.random(in: 0...100)) } )
    .print("Random")

cancellable1 = pub
   .sink { print ("Stream 1 received: \($0)")}
cancellable2 = pub
   .sink { print ("Stream 2 received: \($0)")}

// Random: receive value: (("First", 9))
// Stream 1 received: ("First", 9)
// Random: receive value: (("Second", 46))
// Stream 1 received: ("Second", 46)
// Random: receive value: (("Third", 26))
// Stream 1 received: ("Third", 26)
// Random: receive value: (("First", 9))
// Stream 2 received: ("First", 9)
// Random: receive value: (("Second", 46))
// Stream 2 received: ("Second", 46)
// Random: receive value: (("Third", 26))
// Stream 2 received: ("Third", 26)

기존에는 이렇게 각 pub에서의 발행이 2번 이루어졌어요.

지금이야 크게 문제가 없지만 만약 통신이나 방대한 서비스에 대해 여러 구독자가 구독을 하고 있으면 낭비겠죠?

같은걸 여러 구독자때문에 여러번 발행하는 형태이니까요!

그래서 multicast를 사용하면 이롭습니다🙌

 

multicast도 이제 쉽게 코드로 보면서 감을 찾아보시죠!

(코드는 공식문서에서 예제 코드를 가져왔습니다)

 

[multicast 사용]

let pub = ["First", "Second", "Third"].publisher
    .map( { return ($0, Int.random(in: 0...100)) } )
    .print("Random")
    .multicast { PassthroughSubject<(String, Int), Never>() }

cancellable1 = pub
   .sink { print ("Stream 1 received: \($0)")}
cancellable2 = pub
   .sink { print ("Stream 2 received: \($0)")}
   
pub.connect()

// Random: receive value: (("First", 9))
// Stream 2 received: ("First", 9)
// Stream 1 received: ("First", 9)
// Random: receive value: (("Second", 46))
// Stream 2 received: ("Second", 46)
// Stream 1 received: ("Second", 46)
// Random: receive value: (("Third", 26))
// Stream 2 received: ("Third", 26)
// Stream 1 received: ("Third", 26)

짜란 일단 결과를 보면 한번 발행으로 여러 구독자에서 받아줍니다!

편안....

달라진 부분을 보면 publisher에서 multicast 클로저 블럭을 넣어줘요.

그리고 마지막에 connect()를 해줘야 정상적으로 사용이 됩니다!

connect 관련해서는 왜 그런지 생각해보면 multicast를 해주면 Publishers.Multicast로 타입이 변환되요.

이 Multicast 타입을 까보면 ConnectablePublisher를 채택하고 있습니다.

저 친구의 특성상 connect()를 호출해야 요소들을 방출합니다.

이 부분은 다음 포스팅에서 ConnectablePublisher이 뭔지 조금 더 자세히 알아보기로 하죠!🙋🏻

 

그러면 이제 share가 남았네요?
뭘까요 이녀석은 도대체🙃

share

공식문서의 설명부터 보죠!

업스트림 게시자의 출력을 여러 구독자와 공유하도록 해주는 메서드라고 합니다.

multicast와 유사해보이죠? 이렇게 묶어서 포스팅하는것도 이유가 될것 같네요🙌

func share() -> Publishers.Share<Self>

선언은 요렇습니다.

이 메서드를 호출하여 반환된 Publisher는 여러 Subscriber들을 지원하고 모두 변경되지 않은 요소들과 완료 여부 상태를 받습니다.

우리 위에서 multicast할때 ConnectablePublisher를 따르는 Multicast 타입이 됨으로 connect()를 꼭 호출해줘야 발행된다고 했죠?

이 친구는 그런거 없습니다.

왜냐면, 내부적으로 autoconnect()를 호출하고 있기 때문이죠!

즉 connect()를 호출하지 않아도 Subscriber에서 구독해주는 순간부터 받고 있게 됩니다.

대신 이것때문에 중요한게 있습니다.

여러 구독자가 sink해주는 시점이 다를텐데 첫 구독자가 sink해줄때 바로 방출되게 된다면 안되겠죠?

그래서 delay를 걸어줘야합니다.

 

"Talk is cheap. Show me the code."

 

네.. 이 문구 참 맘에 드네요🙃

(동일하게 공식문서의 예제 코드를 가져왔습니다.)

let pub = (1...3).publisher
    .delay(for: 1, scheduler: DispatchQueue.main)
    .map( { _ in return Int.random(in: 0...100) } )
    .print("Random")
    .share()

cancellable1 = pub
    .sink { print ("Stream 1 received: \($0)")}
cancellable2 = pub
    .sink { print ("Stream 2 received: \($0)")}

// Random: receive value: (20)
// Stream 1 received: 20
// Stream 2 received: 20
// Random: receive value: (85)
// Stream 1 received: 85
// Stream 2 received: 85
// Random: receive value: (98)
// Stream 1 received: 98
// Stream 2 received: 98

publisher를 생성할때 delay를 걸어주는 이유는 위에 설명했습니다.

이어 share()만 호출하여 붙여줘 이 Publisher의 타입을 Publiser.Share로 만들어줍니다.

그리고 여러 구독자에서 이 publisher를 구독하면 하나의 발행에서 위에서 했던 multicast의 방식과 동일하게 진행됩니다!

둘이 참 비슷하네요🙌

마무리

이렇게 multicast와 share에 대해 알아보았습니다!

둘다 비슷한 쓰임이라 더 쉬웠던 이해흐름이 되었어요ㅎㅎ

아직 갈길멀다 combine......🏃🏻

[참고자료]

https://developer.apple.com/documentation/combine/publisher/multicast(_:) 

 

Apple Developer Documentation

 

developer.apple.com

https://developer.apple.com/documentation/combine/publisher/share() 

 

Apple Developer Documentation

 

developer.apple.com