[Combine] Subject에 대해 알아보기

haram·2025년 2월 1일
0
post-thumbnail

Subject를 사용하는 이유

내가 처음 Combine을 공부할 때 Publisher은 무조건 값을 소유 해야 하고 해당 값이 어떤 이벤트에 의해서 변화 할 때 subscriber에 값을 방출하는 것인 줄 잘못 알고 있었다.

하지만 combine의 값 방출 시점은 값의 변화가 아닌 이벤트 발생 시점과 더 가깝다는 사실을 알게 되었다.

만약 이벤트 발생시점을 외부에서 호출을 통해 제어하고 싶다면 어떻게 해야할까?

이런 경우에 사용 할 수 있는 것이 subject이다.

Subject프로토콜의 정의

A publisher that exposes a method for outside callers to publish elements.
외부 호출자를 통해 값을 방출시키기 위한 함수를 노출시키는 puslisher이다.

A subject is a publisher that you can use to ”inject” values into a stream, by calling its send(_:) method. This can be useful for adapting existing imperative code to the Combine model.
subject는 send메서드를 호출함으로써 스트림에 값을 삽입할 수 있는 publisher이다.
이것은 기존 명령형 코드를 combine모델에 적용하는데 유용할 수 있다.

Subject프로토콜 구현체

  • PassthroughSubject
    하위 subscriber에게 브로트캐스트 방식으로 값을 전파하는 publisher이다.

  • CurrentValueSubject
    내부에 값을 가지고 있고 내부 값이 변화하면 하위 subscriber들에게 값을 전파한다.

두 구현체의 차이는 무엇일까?

PassthroughSubject는 send메서드를 통해 들어온 값은 단순히 방출시키는 역활만 한다면

CurrentValueSubject는 send메서드를 통해 들어온 값을 내부에 저장하고 저장된 값을 방출시키는 과정을 거친다.

구현예제

  • 출력결과를 보면 두가지 Subject모두 sub1을 생성하고 .finished값을 방출한다.
    이때 CurrentValueSubject는 값을 소유하기 때문에 sub1을 생성한 직후 subject에 저장된 초기값을 바로 방출하지만 PassThroughSubject는 completion결과만 출력되는 것을 볼수있다.

  • PassThroughSubject예제

    func callPassthrough(){
        let passthroughSubject = PassthroughSubject<String, Never>()

        let sub1 = passthroughSubject
            .sink(receiveCompletion: { print("1 번째 sink completion: \($0)") },
                  receiveValue: { print("1 번째 sink value: \($0)") })

        passthroughSubject.send(completion: .finished)

        let sub2 = passthroughSubject
            .sink(receiveCompletion: { print("2 번째 sink completion: \($0)") },
                  receiveValue: { print("2 번째 sink value: \($0)") })

        // 현재 Subscriber들에게 모두 보냄
        passthroughSubject.send("두번째 값")

    }

출력결과

1 번째 sink completion: finished
2 번째 sink completion: finished

  • CurrentValueSubject
    func callCurrentValue(){
        let currentValueSubject = CurrentValueSubject<String, Never>("첫번째 값")

        let sub1 = currentValueSubject
            .sink(receiveCompletion: { print("1 번째 sink completion: \($0)") },
                  receiveValue: { print("1 번째 sink value: \($0)") })

        currentValueSubject.send(completion: .finished)

        let sub2 = currentValueSubject
            .sink(receiveCompletion: { print("2 번째 sink completion: \($0)") },
                  receiveValue: { print("2 번째 sink value: \($0)") })

        // 현재 Subscriber들에게 모두 보냄
        currentValueSubject.send("두번째 값")

        print(currentValueSubject.value)
    }

출력값

1 번째 sink value: 첫번째 값
1 번째 sink completion: finished
2 번째 sink completion: finished
첫번째 값

0개의 댓글