Mehrere Publisher kombinieren - wurzelsand/swift-memos GitHub Wiki
-
append
,prepend
merge
combineLatest
zip
switchToLatest
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.
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
- Mit
prepend
lassen sich Werte oder Publisher auch vor den Publisher einfü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.
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
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.
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
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.
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
- 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
.
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.
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
- Nur wenn beide
completions
abgeschickt wurden, ist derSwitchToLatest
-Publisher beendet undreceiveCompletion
wird aufgerufen.