Reputation: 14349
As we know, usually publishers are struct. What will change if it is a class?
Let's consider we have 1 publisher which emits 1 value and 2 subscribers, which subscribes to it.
let p1 = Just(20)
let s1 = p1.print().sink { _ in }
let s2 = p1.print().sink { _ in }
// s1 - receive value: (20)
// s2 - receive value: (20)
in print logs, we can see that both subscribers got value (20).
If we open the documentation of share() operator, we will see
share() - Returns a publisher as a class instance.
So it just changes semantic of the publisher from value to reference. In our example, we don't pass p1
publisher to any function or assign to any object and that's why for me there is no difference publisher is struct or class ...
But if I add share()
operator behavior will be different, s2
won't get value.
let p1 = Just(20).share() // !
let s1 = p1.print().sink { _ in }
let s2 = p1.print().sink { _ in }
// s1 - receive value: (20)
I saw some examples with URLSession.shared.dataTaskPublisher(\_: URL)
or with some "delayed" publishers, when s2
also gets value, but it's still unclear for me how just changing semantic of publisher change its behaviour in such way.
Upvotes: 10
Views: 4768
Reputation: 672
In my option, if you don't add .share()
, there will be two event streams subscribed by two subscribers. If you add .share()
, there will be only one event stream subscribed by two subscribers.
Upvotes: 3
Reputation: 535889
The problem is that you are not using a pipeline where it does make a difference. Consider this example (based on a Cocoa With Love article) where a second subscriber comes online after the publisher has been publishing for some time:
let pub1 = Timer.publish(every: 1, on: .main, in: .default)
let c1 = pub1.connect()
let scan = Publishers.Scan(upstream: pub1, initialResult: 0) { (a, b) -> Int in
a + 1
}
scan.sink { print("a:", $0) }.store(in:&storage)
delay(3) {
scan.sink { print("b:", $0) }.store(in:&self.storage)
}
The point is, there is only one scan
and it is producing 1, 2, 3 when after a delay another subscriber comes along. What will that subscriber get? Will it just pick up where we are now? No. We get this:
a: 1
a: 2
a: 3
a: 4
b: 1
a: 5
b: 2
a: 6
b: 3
...
So in effect we start all over again with our second subscription, because the publisher is a new copy. But if we promote the publisher to a class, we get completely different results:
let pub1 = Timer.publish(every: 1, on: .main, in: .default)
let c1 = pub1.connect()
let scan = Publishers.Scan(upstream: pub1, initialResult: 0) { (a, b) -> Int in
a + 1
}
let scan2 = scan.share() // <--
scan2.sink { print("a:", $0) }.store(in:&storage)
delay(3) {
scan2.sink { print("b:", $0) }.store(in:&self.storage)
}
Now we get this:
a: 1
a: 2
a: 3
a: 4
b: 4
a: 5
b: 5
a: 6
b: 6
a: 7
b: 7
Obviously that's a very significant difference. You can see the same sort of thing if your publisher is a Subject, because that's a class, not a struct.
Upvotes: 23