53

I happen to look into Apple's new Combine framework, where I see two things

PassthroughSubject<String, Failure>

CurrentValueSubject<String, Failure>

Can someone explain to me what is meaning & use of them?

Nasir
  • 1,617
  • 2
  • 19
  • 34
  • 2
    You can start from here [Using Combine](https://heckj.github.io/swiftui-notes/#aboutthisbook) - very helpful. – Asperi Mar 02 '20 at 05:49

6 Answers6

176

I think we can make analogies with real world cases.

PassthroughSubject = A doorbell push button

When someone rings the door, you are notified only if you are at home (you are the subscriber)

PassthroughSubject doesn't have a state, it emits whatever it receives to its subscribers.

CurrentValueSubject = A light switch Someone turns on the lights in your home when you are outside. You get back home and you know someone has turned them on.

CurrentValueSubject has an initial state, it retains the data you put in as its state.

Coşkun Deniz
  • 2,049
  • 1
  • 14
  • 11
92

Both PassthroughSubject and CurrentValueSubject are publishers that conform to the Subject protocol which means you can call send on them to push new values downstream at will.

The main difference is that CurrentValueSubject has a sense of state (current value) and PassthroughSubject simply relays values directly to its subscribers without remembering the "current" value:

var current = CurrentValueSubject<Int, Never>(10)
var passthrough = PassthroughSubject<Int, Never>()

current.send(1)
passthrough.send(1)

current.sink(receiveValue: { print($0) })
passthrough.sink(receiveValue: { print($0) })

You'd see that the current.sink is called immediately with 1. The passthrough.sink is not called because it has no current value. The sink will only be called for values that are emitted after you subscribe.

Note that you can also get and set the current value of a CurrentValueSubject using its value property:

current.value // 1
current.value = 5 // equivalent to current.send(5)

This isn't possible for a passthrough subject.

donnywals
  • 7,241
  • 1
  • 19
  • 27
  • 1
    That's a not good example for `PassthroughSubject`. You are ignoring the cancellable return on `passthrough.sink(receiveValue: { print($0) })`, so it will never print anything, even if you send some value afterward. You should save the return to a variable. – mcatach Nov 04 '21 at 05:01
  • That's not correct. Since all of this runs synchronously, the initial value of 1 will be printed immediately. You'd be correct if the subscription needs to be longer lived but for demonstrating the difference between the two subjects this is perfectly fine. – donnywals Nov 11 '21 at 09:01
  • the value 1 comes from the `CurrentValueSubject`. If you add the code `passthrough.send(90)` on the last line, you will never get 90 printed. That's my point. – mcatach Nov 11 '21 at 15:01
18

PassthroughSubject is used for representing events. Use it for events like button tap.

CurrentValueSubject is used representing state. Use it for storing any value, say state of switch as off and on.

Note: @Published is kind of CurrentValueSubject.

user3305074
  • 744
  • 7
  • 19
12

PassthroughSubject and CurrentValueSubject are both Publishers — a type introduced by Combine — that you can subscribe to (performing operations on values when values are available).

They both are designed to make it easy to transfer to using the Combine paradigm. They both have a value and an error type, and you can "send" values to them (making the values available to all subscribers)

The main difference between the two that I've seen is that CurrentValueSubject starts with a value, while PassthroughSubject does not. PassthroughSubject seems easier to grasp conceptually, at least for me.

PassthroughSubject can easily be used in place of a delegate pattern, or to convert an existing delegate pattern to Combine.

//Replacing the delegate pattern
class MyType {
    let publisher: PassthroughSubject<String, Never> = PassthroughSubject()

    func doSomething() {
        //do whatever this class does

        //instead of this:
        //self.delegate?.handleValue(value)

        //do this:
        publisher.send(value)
    }
}

//Converting delegate pattern to Combine
class MyDel: SomeTypeDelegate {
    let publisher: PassthroughSubject<String, Never> = PassthroughSubject()

    func handle(_ value: String) {
        publisher.send(value)
    }
}

Both of these examples use String as the type of the value, while it could be anything.

Hope this helps!

Sam
  • 2,350
  • 1
  • 11
  • 22
5

PassthroughSubject is suitable for event like tap action

CurrentValueSubject is suitable for state

DeyaEldeen
  • 10,847
  • 10
  • 42
  • 75
LiangWang
  • 8,038
  • 8
  • 41
  • 54
1

Already a lot of good answers posted in this thread, just thought of adding this answer for someone who is coming from using RxSwift.

PassThroughSubject is like PublishSubject where it broadcasts an event to its subscribers, probably with some value passed along.

CurrentValueSubject is similar to BehaviorRelay where a single value is persisted with the subject instance and passed along during the event broadcast.

Adithya
  • 4,545
  • 3
  • 25
  • 28