Github.com/agelessman/…

The data merge mentioned in this section is essentially a merge of multiple publishers, which can be divided into three categories according to the different merge methods:

  • combineLatest
  • merge
  • zip

combineLatest

A picture is worth a thousand words, and the above picture illustrates the core idea of combineLatest:

  • It merged two publishers
  • The output data is grouped and the data structure is tuple

Grouping data is both a strength and a weakness, and I won’t go into that. The weakness is that if the data can’t be grouped, it won’t output. The normal code is as follows:

let firstPublisher = PassthroughSubject<Int.MyCustomError> ()let secondPublisher = PassthroughSubject<String.MyCustomError>()

firstPublisher
    .combineLatest(secondPublisher)
    .sink(receiveCompletion: { completion in
        print("It's over.")
        switch completion {
        case .finished:
            print("Complete")
        case .failure(let error):
            print("Error:\(error.localizedDescription)")
        }
    }, receiveValue: { someValue in
        print("someValue: \(someValue)")
    })
    .store(in: &cancellables)

firstPublisher.send(1)
secondPublisher.send("a")
firstPublisher.send(2)
Copy the code

Print result:

someValue: (1."a")
someValue: (2."a")
Copy the code

Let’s first ask Second Publisher not to output data and see what the output is.

.
firstPublisher.send(1)
/// secondPublisher.send("a")
firstPublisher.send(2)
Copy the code

Running the above code does not produce any output, which means that if either publisher has no data, pipline will not produce any data.

So what happens if Second Publisher sends the.finished event after sending data? Let’s modify the code slightly:

.
firstPublisher.send(1)
secondPublisher.send("a")
secondPublisher.send(completion: Subscribers.Completion.finished)
firstPublisher.send(2)
Copy the code

Print result:

someValue: (1."a")
someValue: (2."a")
Copy the code

As you can see, even after the Second Publisher sends the.finished event, the entire Pipline continues to execute, using the last data from the Publisher when merging data.

Further, what happens if Second Publisher sends a. Failure event? Modify code:

enum MyCustomError: Error {
    case custom
}
.
firstPublisher.send(1)
secondPublisher.send("a")
secondPublisher.send(completion: Subscribers.Completion.failure(MyCustomError.custom))
firstPublisher.send(2)
Copy the code
someValue: (1."a"The end of the)Error.Copy the code

As you can see, the Pipline is very sensitive to errors and terminates the Pipline as soon as an error is detected.

Does that make sense? To sum up: The core idea of combineLatest is to combine the data from two publishers. Normally, any new data generated from two publishers will be combined and output, and pipline will be terminated immediately if any error occurs.

CombineLatest can combine 3 and 4 publishers as well. The combineLatest can combine 3 and 4 publishers as well.


merge

Merge is also used to merge publisers, up to 8 and at least 2, but it has its own feature, combineLatest combines the latest data from each publiser into a tuple, and merge outputs new data whenever it receives it.

Merge is useful for merging multiple unordered publishers scenarios. As for sending a publisher.finishedor.failureThe logic of events is the same as combineLatest.

cancellables = Set<AnyCancellable> ()let pub1 = PassthroughSubject<Int.Never> ()let pub2 = PassthroughSubject<Int.Never> ()let pub3 = PassthroughSubject<Int.Never> ()let pub4 = PassthroughSubject<Int.Never> ()let pub5 = PassthroughSubject<Int.Never> ()let pub6 = PassthroughSubject<Int.Never> ()let pub7 = PassthroughSubject<Int.Never> ()let pub8 = PassthroughSubject<Int.Never>()

pub1
    .merge(with: pub2, pub3,
           pub4, pub5,
           pub6, pub7, pub8)
    .sink(receiveCompletion: { completion in
        print("It's over.")
        switch completion {
        case .finished:
            print("Complete")
        case .failure(let error):
            print("Error:\(error.localizedDescription)")
        }
    }, receiveValue: { someValue in
        print("someValue: \(someValue)")
    })
    .store(in: &cancellables)

pub1.send(1)
pub2.send(2)
pub3.send(3)
pub4.send(4)
pub5.send(5)
pub6.send(6)
pub7.send(7)
pub8.send(8)
Copy the code

zip

Zip is very similar to combineLatest in that it returns groups of data by default, but the biggest difference is that ZIP doesn’t use latest, it uses new values.

Looking at the figure above, when pub2 returns data 4, pipline returns (1, 4), indicating that the data used in the data flow is a new value that has never been used in the two Publishers and follows a first-in, first-out rule. This is the biggest feature of ZIP.

This feature can be useful in scenarios where, for example, you need to wait for two asynchronous requests to complete and then get two pieces of data to continue performing other tasks, which is a good fit for ZIP.

let pub1 = PassthroughSubject<Int.Never> ()let pub2 = PassthroughSubject<Int.Never>()

pub1
    .zip(pub2)
    .sink(receiveCompletion: { completion in
        print("It's over.")
        switch completion {
        case .finished:
            print("Complete")
        case .failure(let error):
            print("Error:\(error.localizedDescription)")
        }
    }, receiveValue: { someValue in
        print("someValue: \(someValue)")
    })
    .store(in: &cancellables)

pub1.send(1)
pub1.send(2)
pub1.send(3)
pub2.send(4)
pub2.send(5)
Copy the code

Zip can merge up to four publishers. The principle is the same, but we’ll skip over it here. Zip also has a feature that provides a closure parameter that allows us to transform data.

As you can see, pipline’s output type is String, and we can freely convert the data using closures, which is very useful in normal development. The code is as follows:

let numberPublisher = PassthroughSubject<Int.Never> ()let emojiPublisher = PassthroughSubject<String.Never>()

numberPublisher
    .zip(emojiPublisher) { number, emoji -> String in
        String(repeating: emoji, count: number)
    }
    .sink(receiveCompletion: { completion in
        .
    }, receiveValue: { someValue in
        print("someValue: \(someValue)")
    })
    .store(in: &cancellables)

numberPublisher.send(2)
emojiPublisher.send("😂")
Copy the code

Print result:

SomeValue: 😂 😂Copy the code

** To summarize, ZIP features new values for each publisher and the ability to map data. In real development, combineLatest, Merge, zip can be considered when multiple asynchronous tasks need to be merged.