Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Understanding share() in Combine

Tags:

swift

combine

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.

like image 308
Tikhonov Aleksandr Avatar asked Jan 04 '20 17:01

Tikhonov Aleksandr


People also ask

What are publishers in combine?

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.

How does swift combine work?

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.

What is subject in combine?

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.

What is AnyPublisher in Swift?

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.

What are shares and how do they work?

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!

What is an example of a 1-for-2 stock merger?

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.

What happens to shares of a company after a merger?

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.

How do publishers work in combine?

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.


2 Answers

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.

like image 102
matt Avatar answered Sep 23 '22 02:09

matt


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.

like image 25
Changwei Avatar answered Sep 22 '22 02:09

Changwei