Content overview
Preface
Use makeConnectable() and connect() to manually control the release
Use the autoconnect() operator to automatically connect
to sum up
Preface
Using Connectable Publisher
, you can decide when the publisher starts to send subscription elements to subscribers. So why do we need to do this?
Use sink(receiveValue:)
can immediately begin to receive subscription element, but this is probably not what you want. When multiple subscribers subscribe to the same publisher, it may happen that one subscriber receives the subscription content while the other subscriber cannot receive it.
For example, when you initiate a network request and create a publisher for the request and the subscribers connected to the publisher.
Then, the subscriber's subscription operation triggered the actual network request. At some point in time, you connected a second subscriber to this publisher. If the network request has been completed before connecting to the second subscriber, the second subscriber will only receive the completion event and will not receive the response result of the network request. At this time, this result will not be what you expected.
In Combine
the process of using, we often need to face these problems. Let's figure out how to deal with this type of problem now~
Use makeConnectable() and connect() to control release
ConnectablePublisher
It is a protocol type that prevents the publisher from publishing elements before you are ready.
/// A connectable publisher, which provides an explicit way to connect and unsubscribe /// /// Use `makeConnectable()` to create a `ConnectablePublisher from any publisher whose failure type is `Never` `@available(OSX 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) public protocol ConnectablePublisher: Publisher {/// Connect to the publisher and return a `Cancellable` instance for canceling the publication /// ///- Return value: a `Cancellable` instance func connect() -> Cancellable} used to cancel publishing
Before you explicitly call the connect()
method, one ConnectablePublisher
will not send any elements.
Now, let us use ConnectablePublisher
to solve the problem of network request example mentioned above it!
After both subscribers are connected to the publisher, it is called connect()
, and then the network request is triggered. This way, race conditions can be avoided and both subscribers can receive data.
For use in your Combine code ConnectablePublisher
, you can use makeConnectable()
operators to pack the current publisher into a Publishers.MakeConnectable
structure instance.
As shown in the code below:
class ConnectablePublisherDemo { private var cancellable1: AnyCancellable? private var cancellable2: AnyCancellable? private var connection: Cancellable? func run() { let url = URL(string: "https://ficow.cn")! let connectable = URLSession.shared .dataTaskPublisher(for: url) .map(\.data) .catch() { _ in Just(Data()) } .share() .makeConnectable() // 阻止发布者发布内容 cancellable1 = connectable .sink(receiveCompletion: { print("Received completion 1: \($0).") }, receiveValue: { print("Received data 1: \($0.count) bytes.") }) DispatchQueue.main.asyncAfter(deadline: .now() + 1) { self.cancellable2 = connectable.sink(receiveCompletion: { log("Received completion 2: \($0).") }, receiveValue: {log("Received data 2: \($0.count) bytes.") })} DispatchQueue.main.asyncAfter(deadline: .now() + 2 ) {// Explicitly start the publication. The return value needs to be strongly quoted and can be used to cancel the publication (the cancel method is actively called or the return value is destroyed) self.connection = connectable.connect()}}}
Note that makeConnectable()
there is an operator before the share()
operator! Excuse me, what does this operator do?
Use the autoconnect() operator to automatically connect
Some Combine
publishers have implemented ConnectablePublisher
protocols, such as: Publishers.Multicast
and Timer.TimerPublisher
. When using these publishers, if you don't need to configure the publisher or connect multiple subscribers, you need to call the connect()
method explicitly .
In this case, ConnectablePublisher
it provided autoconnect()
the operator. When a subscriber subscribe(_:)
connects to the publisher through a method, the connect()
method will be called immediately.
let cancellable = Timer.publish(every: 1, on: .main, in: .default) .autoconnect() .sink() { date in print ("Date now: \(date)") }
It is used in the above code example autoconnect()
, so subscribers can immediately receive the elements sent by the timer. If not autoconnect()
, we need to manually call the connect()
method at some point .
to sum up
Combine
It provides us with a very powerful asynchronous programming function, but this also comes at a price. We need to be well aware Combine
of the problems that may be encountered during use . If “坑”
you start on the road without understanding these , the probability of making mistakes will be very high, and the cost of making mistakes will be very high.
Reprinted from https://www.cnblogs.com/#p53