Skip to content

Mehrere Publisher kombinieren

wurzelsand edited this page May 3, 2022 · 7 revisions

Mehrere Publisher kombinieren

Themen

  • append, prepend
  • merge
  • combineLatest
  • zip
  • switchToLatest

Aufgabe 1: Werte oder Publisher hinzufügen

Ich habe zwei Publisher:

let publisher1 = (1...3).publisher
let publisher2 = (4...6).publisher

publisher2 soll hinter publisher1 angehängt werden. Anschließend hänge ich noch die Werte 7, 8 und 9 an.

Ausführung

import Combine

var subscriptions = Set<AnyCancellable>()

let publisher1 = (1...3).publisher
let publisher2 = (4...6).publisher


publisher1
    .append(publisher2)
    .append(7, 8, 9)
    .sink {
        print($0)
    }
    .store(in: &subscriptions)

Ausgabe:

1
2
3
4
5
6
7
8
9
Program ended with exit code: 0

Anmerkungen

  • Mit prepend lassen sich Werte oder Publisher auch vor den Publisher einfügen.

Aufgabe 2: Publisher mit merge zusammenfügen

Während append aus Aufgabe 1 Publisher nacheinander abarbeitet, möchte ich nun Publisher so zusammenfügen, dass die Elemente in der Reihenfolge abgearbeitet werden, in der sie zeitlich gesendet werden.

Schreibe ein Programm, dass die Zahlen von 1 bis 6 über zwei getrennte Publisher veröffentlicht: Der publisher1 soll alle ungeraden Zahlen senden, der publisher2 alle geraden. Vereinige beide Publisher, so dass sie von derselben Subscription abgearbeitet und ausgegeben werden.

Ausführung

import Foundation
import Combine

let publisher1 = PassthroughSubject<String, Never>()
let publisher2 = PassthroughSubject<String, Never>()

let subscription = publisher1.merge(with: publisher2)
    .sink { print("subscription:", $0) }

for i in 1...6 {
    if i.isMultiple(of: 2) {
        publisher2.send("publisher2: \(i)")
    } else {
        publisher1.send("publisher1: \(i)")
    }
}

Ausgabe:

subscription: publisher1: 1
subscription: publisher2: 2
subscription: publisher1: 3
subscription: publisher2: 4
subscription: publisher1: 5
subscription: publisher2: 6
Program ended with exit code: 0

Aufgabe 3: combineLatest

Ich möchte Werte, die in einem Zusammenhang stehen, so kombinieren, dass ich immer über den aktuellen Stand informiert werde.

Hier sollen die Stunde des Tages und Ort eines Flugzeugs auf dem Flug von Frankreich nach Polen im Zusammenhang stehen: Zur fünften Stunde befindet sich das Flugzeug noch nicht in der Luft. Zur sechsten Stunde befindet es sich zunächst über Frankreich dann über Deutschland. Zur siebten Stunde ist es immer noch über Deutschland und später über Polen. Die achte Stunde beginnt und das Flugzeug befindet sich immer noch über Polen.

Ausführung

import Foundation
import Combine

let timePublisher = PassthroughSubject<Int, Never>()
let countryPublisher = PassthroughSubject<String, Never>()

let subscription = timePublisher.combineLatest(countryPublisher)
    .sink { print("\($0)th hour: airplane above \($1)") }

timePublisher.send(5)
timePublisher.send(6)
countryPublisher.send("France")
countryPublisher.send("Germany")
timePublisher.send(7)
countryPublisher.send("Poland")
timePublisher.send(8)

Ausgabe:

6th hour: airplane above France
6th hour: airplane above Germany
7th hour: airplane above Germany
7th hour: airplane above Poland
8th hour: airplane above Poland
Program ended with exit code: 0

Aufgabe 4: zip

Ich habe zwei Publisher die zusammengehörende Elemente publizieren. Auch wenn beide Publisher nicht ganz synchron laufen sollten, müssen alle zusammengehörenden Elemente als Tuple zusammengefügt und weitergeschickt werden.

Ich stelle mir eine Fabrik für Spielzeugtiere vor: In ihr werden an zwei Fließbändern Köpfe und Körper getrennt voneinander produziert. Unabhängig davon wie unterschiedlich schnell die Fließbänder produzieren, dürfen aber nur zusammengehörende Köpfe und Körper zusammengesetzt werden.

Ausführung

import Foundation
import Combine

struct Head {
    let description: String
}

struct Body {
    let description: String
}

let animals = ["duck", "cat", "dog", "horse"]

let headPublisher = PassthroughSubject<Head, Never>()
let bodyPublisher = PassthroughSubject<Body, Never>()

let subscription = headPublisher.zip(bodyPublisher)
    .sink { print($0.description, "+", $1.description) }

var animalsHeadIterator = animals.makeIterator()
var animalsBodyIterator = animals.makeIterator()

for _ in 1...9 {
    if Bool.random() {
        if let animal = animalsHeadIterator.next() {
            let head = Head(description: animal + "-head")
            headPublisher.send(head)
        }
    } else {
        if let animal = animalsBodyIterator.next() {
            let body = Body(description: animal + "-body")
            bodyPublisher.send(body)
        }
    }
}

Ausgabe:

duck-head + duck-body
cat-head + cat-body
dog-head + dog-body
horse-head + horse-body

Anmerkungen

  • Es kann durchaus passieren, dass das eine Fließband noch nicht mit seiner Produktion fertig ist und daher das Tier nicht zusammengesetzt werden kann. Dann fehlt z. B. horse-head mit + horse-body.

Aufgabe 5: Zwischen Publishern wechseln

Ich habe zwei Publisher in Form von PassthroughSubjects:

let publisher1 = PassthroughSubject<Int, Never>()
let publisher2 = PassthroughSubject<Int, Never>()

Der eine sendet immer negative Zahlen, der andere positive. Es sollen abwechselnd immer nur von einem der Publisher Werte empfangen werden.

Ausführung

import Combine

var subscriptions = Set<AnyCancellable>()

let publisher1 = PassthroughSubject<Int, Never>()
let publisher2 = PassthroughSubject<Int, Never>()

let publisherContainer = PassthroughSubject<PassthroughSubject<Int, Never>, Never>()

publisherContainer
    .switchToLatest()
    .sink(receiveCompletion: { print("completion:", $0)},
          receiveValue: { print("value:", $0) })
    .store(in: &subscriptions)

publisher1.send(1)
publisher2.send(-1)
publisherContainer.send(publisher1)
publisher1.send(2)
publisher2.send(-2)
publisherContainer.send(publisher2)
publisher1.send(3)
publisher2.send(-3)
// #1:
publisher2.send(completion: .finished)
publisherContainer.send(completion: .finished)

Ausgabe:

value: 2
value: -3
completion: finished
Program ended with exit code: 0

Anmerkungen

  1. Nur wenn beide completions abgeschickt wurden, ist der SwitchToLatest-Publisher beendet und receiveCompletion wird aufgerufen.