Blockchain ク gainst to the list

This article uses the latest version of xcode 13 + macOS Big sur + iOS15

Introduction to the

In Apple’s own words, Combine provides a responsive declarative framework for apps to handle events. Instead of inheriting the Delegate callback or using the completion closure argument, you can create a chain of handlers from the source of the event. Each link of the processing chain is a Combine operation, and each operation will perform specific processing on the content passed by the previous operation.

As abstract as it sounds, for a loose example, use URLSession to request the network to parse JSON data

  • Previous practice:

    let task = URLSession.shared.dataTask(with: URL(string:"https://www.url.com")!) { data, resp, error in
        guard error = = nil else{
            // Processing error
            return
        }
        guard data ! = nil else{
            // Processing error
            return
        }
        
        let decoder = JSONDecoder(a)if let json = try? decoder.decode(JsonTarget.self, from: data!) {self.response = json
        }else{
          self.response = Json(code: -1)
        }
    }
    task.resume()
    Copy the code
  • Combine:

    let publisher =  URLSession.shared.dataTaskPublisher(for:URL(string:"https://www.url.com")!)
    let cancellable =
        .map({return $0.data})
        .decode(type: JsonTarget.self, decoder: JSONDecoder())
        .replaceError(with: JsonTarget(code: -1))
        .assign(to: \.resposne, on: self)
    Copy the code
    • URLSession.shared.dataTaskPublisherWe’re going to return Publisher
    • map decodeandreplaceErrorIs operation (Operator)
    • assignYes Subscriber
    • cancellableIs aCancellableCan use thecancel()Method to cancel the related operation.

    Combine’s process is as follows:

  • First the subscriber requests a subscription.
  • The publisher then returns the Subscription
  • Again the subscriber requests the data
  • Publishers continuously publish data to subscribers
  • The publisher sends a completion event to end the process

The Publisher (Publisher)

Publisher is an important agreement of the Combine framework. Publisher is publishing events to one or more subscribers. Using a NotificationCenter programming when the NotificationCenter. Default. The post to publish event, the publish event object is equivalent to a Publisher, it’s just not using Combine framework.

A Publisher can pass events to one or more Subscriber instances. Publisher contains the Output type and the Failure error type. This Output type and Failure type can be converted by an Operator, Subscriber Intput and Failure types can only be subscribed if they match Output type and Failure type of Publisher or Output type and Failure type after Operator conversion.

Publisher agreement

First look at the Publisher agreement:

public protocol Publisher {
    associatedtype Output

    associatedtype Failure : Error
    
    func receive<S> (subscriber: S) where S : Subscriber.Self.Failure = = S.Failure.Self.Output = = S.Input
}

Copy the code

The protocol contains types Output and Failure, and a method to accept subscribers. Simply implement a custom Publisher as follows:

class TestPublisher: Publisher{
    func receive<S> (subscriber: S) where S : Subscriber.Never = = S.Failure.Int = = S.Input {
        subscriber.receive(1)
        DispatchQueue.global().asyncAfter(deadline: DispatchTime.now() + 2){
            subscriber.receive(2)
            subscriber.receive(completion: .finished)
        }
    }
    
    typealias Output = Int
    
    typealias Failure = Never
}


let  testPublisher = TestPublisher()
testPublisher.sink(receiveCompletion: { print("completed".$0)}, receiveValue: {print("received value".$0)})

Copy the code

The output is as follows:

received value 1
received value 2
completed finished
Copy the code

Add a line subscriber. Receive (completion:.finished) after subscriber. Receive (3).

.
DispatchQueue.global().asyncAfter(deadline: DispatchTime.now() + 2){
    subscriber.receive(2)
    subscriber.receive(completion: .finished)
    subscriber.receive(3)}.
Copy the code

Run:

received value 1
received value 2
completed finished
Copy the code

It can be seen that when subscriber.receive(completion:) is called, the process has finished, and the event will be sent after that, and the subscriber cannot receive the event.

Convenience Publishers

Swift has some handy Publisher built in for quick use.

Future

The name means that at some point in the future an event will be emitted and then it will stop.

For example, send a 1 after 10 seconds:

let future = Future<Int.Never>{ promise in
    DispatchQueue.global().asyncAfter(deadline: DispatchTime.now() + 10) {
        promise(.success(1))
    }
}
future.sink(receiveCompletion: { print("Received value".$0)}, receiveValue: { print("Receive value:".$0)}).store(in: &store)
Copy the code

After running for 10s, see the output:

Receive value: 1
Received value finished
Copy the code

Future

: Int indicates the Output type of Publisher, and Never indicates the Failure type. The promise argument in the constructor is used to send the success or failure event.
,>

Just

This is a Publisher that sends out events immediately.

var store = Set<AnyCancellable> ()Just(1).sink(receiveCompletion: { print("Received value".$0)}, receiveValue: { print("Receive value:".$0)}).store(in: &store)
Copy the code

Immediately after running, you see the output:

Receive value: 1
Received value finished
Copy the code

Deferred

This is the delayed creation of Publisher, which is created when the Publisher is subscribed.

For example, release one after 5s:

let date = Date(a)let publisher = Deferred { () -> Just<Int> in
    print("Create Pulisher".0 - date.timeIntervalSinceNow)
    return Just(1)}DispatchQueue.global().asyncAfter(deadline: DispatchTime.now() + 5) {print("Subscribe".0 - date.timeIntervalSinceNow)
    publisher.sink(receiveCompletion: { print("completed".$0.0 - date.timeIntervalSinceNow)}, receiveValue: {print("received value".$0.0 - date.timeIntervalSinceNow)})
}

print("Run".0 - date.timeIntervalSinceNow)
Copy the code

The output is as follows:

Run 0.0022840499877929688
Subscribe 5.497779011726379
Create Pulisher 5.497919082641602
received value 1 5.4987300634384155
completed finished 5.49881899356842
Copy the code

You can see that Just Publisher is created when sink is called.

Empty

Empty does not send anything, but sends a complete event immediately after the subscription.

Fail

Fail does not send anything, but sends the specified Error immediately after subscribing.

Record

Record can first use Record.Recording to Record a series of events, later in the Record sent to subscribers.

For example

var recording = Record<Int.Never>.Recording(a)DispatchQueue.global().asyncAfter(deadline: DispatchTime.now() + 1) {
    let publisher = Record<Int.Never>(recording: recording)
    publisher.sink(receiveCompletion: { print("Received completed".$0)}, receiveValue: { print("Received Value".$0)})}DispatchQueue.global().asyncAfter(deadline: DispatchTime.now() + 2) {
    let publisher2 = Record<Int.Never>(recording: recording)
    publisher2.sink(receiveCompletion: { print("Received 2 completed".$0)}, receiveValue: { print("Received 2 Value".$0)})}DispatchQueue.global().asyncAfter(deadline: DispatchTime.now() + 3) {
    let publisher3 = Record<Int.Never>(recording: recording)
    publisher3.sink(receiveCompletion: { print("Received 3 completed".$0)}, receiveValue: { print("Received 3 Value".$0)})
}


recording.receive(1)
recording.receive(2)
recording.receive(completion: .finished)
Copy the code

Sharing a Recording record sends 1,2 and finish events. Create 3 records to publish the content of a Recording.