티스토리 뷰

728x90
반응형

Timer 쓸 때, 편하게 썼던 autoconnectConnectablePublisher 의 메소드이다.  

ConnectablePublisher 에 대해 자세히 알아보자.

 

 

[1] ConnectablePublisher

ConnectablePublisher 는 connection 을 명확히 지정할 수 있는 publisher 이다. 

즉 connect 메소드를 직접 호출하기 전까지 값을 방출하지 않는다. 

 

값이 방출되기 전에 추가적인 configuration 이나 setup 이 필요할 때,

구독자들이 다 준비된 후 이벤트를 방출하고 싶을 때

유용하다. 

 

autoconnect 는  connection , disconnection 을 자동으로 해주는 메소드이다. 

 

 

 

 

[2] ConnectablePublisher 만들기 

 

failure type 이 Never 인 publisher 에 makeConnectable 을 호출해서 만들 수 있다. 

let subject = PassthroughSubject<Int, Never>()
let connectablePublisher = subject.makeConnectable()

 

혹은 Publishers.MakeConnectable 의 이니셜라이저 로도 만들 수 있긴 하다. 

let subject = PassthroughSubject<Int, Never>()
let connectablePublisher = Publishers.MakeConnectable(upstream: subject)

 

 

makeConnectable 쓰는게 더 일반적인 방법.

 

[3]  예제 

import Combine
import Foundation

let subject = PassthroughSubject<Int, Never>()

// ConnectablePublisher로 변환
let connectablePublisher = subject.makeConnectable()

// 구독자 1 생성
let subscription1 = connectablePublisher
    .sink(receiveValue: { value in
        print("Subscription 1 received value: \(value)")
    })

// 구독자 2 생성
let subscription2 = connectablePublisher
    .sink(receiveValue: { value in
        print("Subscription 2 received value: \(value)")
    })

// ConnectablePublisher 연결
let connection = connectablePublisher.connect()

// 값 방출
subject.send(1)

// [출력]
// Subscription 1 received value: 1
// Subscription 2 received value: 1

// 연결 해제
connection.cancel()

// 더 이상 이벤트가 방출되지 않음
subject.send(2)

 

 

[4]  Timer, Multicast 

Timer.TimerPublisher,  Publishers.Multicast 는 ConnectablePublisher 를 채택하고 있다. 

그리고 share 는  편리하게 쓸  수 있는  Multicast (+PassthroughSubject), autoconnet 조합이다. 

(아래에서 동작비교 할 것이기 때문에 리마인드!) 

 

 

 

 

[5] 동작 비교 - 1  (정적 array) 

 

# Publisher 

let publisher = [1,2,3].publisher

let subscription1 = publisher
    .sink(receiveValue: { value in
        print("Subscription 1 received value: \(value)")
    })

let subscription2 = publisher
    .sink(receiveValue: { value in
        print("Subscription 2 received value: \(value)")
    })


// [출력]
// Subscription 1 received value: 1
// Subscription 1 received value: 2
// Subscription 1 received value: 3

// Subscription 2 received value: 1
// Subscription 2 received value: 2
// Subscription 2 received value: 3

 

 

# ConnectablePublisher

let publisher = [1,2,3].publisher.makeConnectable()

let subscription1 = publisher
    .sink(receiveValue: { value in
        print("Subscription 1 received value: \(value)")
    })

let subscription2 = publisher
    .sink(receiveValue: { value in
        print("Subscription 2 received value: \(value)")
    })

publisher.connect()

// [출력]
// Subscription 1 received value: 1
// Subscription 2 received value: 1

// Subscription 1 received value: 2
// Subscription 2 received value: 2

// Subscription 1 received value: 3
// Subscription 2 received value: 3

 

 

# Multicast 

let publisher = [1,2,3].publisher.multicast {
    PassthroughSubject()
}

let subscription1 = publisher
    .sink(receiveValue: { value in
        print("Subscription 1 received value: \(value)")
    })

let subscription2 = publisher
    .sink(receiveValue: { value in
        print("Subscription 2 received value: \(value)")
    })


publisher.connect()

// [출력]
// Subscription 1 received value: 1
// Subscription 2 received value: 1

// Subscription 1 received value: 2
// Subscription 2 received value: 2

// Subscription 1 received value: 3
// Subscription 2 received value: 3

 

 

# Share

(autoconnect 되기 때문에 타이밍 안맞음)

let publisher = [1,2,3].publisher.share()

let subscription1 = publisher
    .sink(receiveValue: { value in
        print("Subscription 1 received value: \(value)")
    })

let subscription2 = publisher
    .sink(receiveValue: { value in
        print("Subscription 2 received value: \(value)")
    })


// [출력]
// Subscription 1 received value: 1
// Subscription 1 received value: 2
// Subscription 1 received value: 3

 

 

 

[6] 동작 비교 - 2  (Timer)

 

# Timer > connect 

let publisher = Timer.publish(every: 1, on: .main, in: .default)

let subscription1 = publisher
    .sink(receiveValue: { value in
        print("Subscription 1 received value: \(value)")
    })

let subscription2 = publisher
    .sink(receiveValue: { value in
        print("Subscription 2 received value: \(value)")
    })

publisher.connect()


// [출력]
// Subscription 1 received value: 2024-08-04 13:11:58 +0000
// Subscription 2 received value: 2024-08-04 13:11:58 +0000

// Subscription 1 received value: 2024-08-04 13:11:59 +0000
// Subscription 2 received value: 2024-08-04 13:11:59 +0000
...

 

 

# Timer > autoconnect 

let publisher = Timer.publish(every: 1, on: .main, in: .default).autoconnect()

let subscription1 = publisher
    .sink(receiveValue: { value in
        print("Subscription 1 received value: \(value)")
    })

let subscription2 = publisher
    .sink(receiveValue: { value in
        print("Subscription 2 received value: \(value)")
    })

// [출력]
// Subscription 1 received value: 2024-08-04 13:13:10 +0000
// Subscription 2 received value: 2024-08-04 13:13:10 +0000
//
// Subscription 1 received value: 2024-08-04 13:13:11 +0000
// Subscription 2 received value: 2024-08-04 13:13:11 +0000
...

 

 

# Timer > share (이렇게 쓸 필요 없는 데 궁금하여..)

let publisher = Timer.publish(every: 1, on: .main, in: .default)
    .share()

let subscription1 = publisher
    .sink(receiveValue: { value in
        print("Subscription 1 received value: \(value)")
    })

let subscription2 = publisher
    .sink(receiveValue: { value in
        print("Subscription 2 received value: \(value)")
    })


// 출력 안됨.

 

 

# Timer > 혹시 any publisher 로 erase 하면 ?
(그래도 ConnectablePublisher 의 특징이 유지됨.) 

let publisher = Timer.publish(every: 1, on: .main, in: .default)
    .autoconnect()
    .eraseToAnyPublisher()

let subscription1 = publisher
    .sink(receiveValue: { value in
        print("Subscription 1 received value: \(value)")
    })

let subscription2 = publisher
    .sink(receiveValue: { value in
        print("Subscription 2 received value: \(value)")
    })

// [출력]
// Subscription 1 received value: 2024-08-04 13:17:02 +0000
// Subscription 2 received value: 2024-08-04 13:17:02 +0000

// Subscription 1 received value: 2024-08-04 13:17:03 +0000
// Subscription 2 received value: 2024-08-04 13:17:03 +0000
...

 

 

 

 

반응형
댓글