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.
Overview. A publisher delivers elements to one or more Subscriber instances. The subscriber's Input and Failure associated types must match the Output and Failure types declared by the publisher. The publisher implements the receive(subscriber:) method to accept a subscriber.
The Combine framework provides a declarative Swift API for processing values over time. These values can represent many kinds of asynchronous events. Combine declares publishers to expose values that can change over time, and subscribers to receive those values from the publishers.
Overview. 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.
AnyPublisher is a concrete implementation of Publisher that has no significant properties of its own, and passes through elements and completion values from its upstream publisher. Use AnyPublisher to wrap a publisher whose type has details you don't want to expose across API boundaries, such as different modules.
Shares are issued by a company to raise money (capital) to help plan for future projects or because the owner/s of the company want a big lump sum of money for themselves as a reward for the hard work they have put into building up the company!
For example, Company A and Company E form an agreement to undergo a 1-for-2 stock merger. Company E's shareholders will receive one share of Company A for every two shares they currently own in the process.
Company E shares will stop trading, and the outstanding shares of Company A will increase after the merger is complete when the share price of Company A will depend on the market's assessment of the future earnings prospects for the newly merged entity. It is uncommon for a stock-for-stock merger to take place in full.
All publishers that you use in Combine are built by composing the same building blocks in one way or the other. What's important is that you understand what these building blocks are, and what role they fulfill. To summarize, when a subscriber wants to subscribe to a publisher, the publisher creates a subscription.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With