Combine framework, from 0 to 1 —— 2. Control when to send through ConnectablePublisher

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.


Picture of alt


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!


ConnectablePublisher


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


Guess you like

Origin blog.51cto.com/14939410/2536003