Combine ist ein reaktives Programmier-Framework von Apple, das mit iOS 13 eingeführt wurde. Laut Apple Documentation, 2025 bietet Combine eine einheitliche deklarative API zur Verarbeitung asynchroner Ereignisse. Publisher definiert die Datenquelle, und Subscriber abonniert sie, um Daten zu erhalten.
Wichtige Erkenntnisse
Combine ist ein deklaratives Swift-Framework zur Verarbeitung asynchroner Ereignisse im Zeitverlauf. Im Gegensatz zu callback-basierten Ansätzen ermöglicht Combine die Beschreibung von Datenverarbeitungspipelines durch eine Kette von Operatoren. Das Framework ist in Foundation (URLSession, Timer, NotificationCenter) und SwiftUI (ObservableObject, @Published) integriert.
Jeder Typ, der das Publisher-Protokoll implementiert, muss Output (Wertetyp) und Failure (Fehlertyp) definieren. Ein Publisher ist erst aktiv, wenn er abonniert wird — er beginnt erst nach dem Aufruf von subscribe mit dem Emittieren von Ereignissen. Dies ist eine kalte Semantik, die für reaktive Streams charakteristisch ist und die Effizienz verbessert.
Subscriber empfängt Ereignisse über drei Methoden: receive(subscription:) — Bestätigung des Abonnements mit Demand-Management; receive(_:) — Empfang eines neuen Werts; receive(completion:) — Abschluss des Streams mit Erfolg oder Fehler. Demand gibt an, wie viele Werte der Subscriber zu akzeptieren bereit ist — dies ist der Backpressure-Mechanismus.
Apple bietet viele integrierte Publisher: Just emittiert einen einzelnen Wert und schließt ab, Future — ein asynchrones Ergebnis mit einem Closure, Deferred — verzögerte Erstellung eines Publishers bis zum Abonnement. URLSession.dataTaskPublisher wandelt eine Netzwerkanfrage in einen reaktiven Stream um. Timer.publish erstellt einen periodischen Timer. NotificationCenter.default.publisher verwandelt Benachrichtigungen in einen Publisher. Diese Integrationen ermöglichen das Abonnieren von Ereignissen ohne manuelle Brücke.
In Combine sind Publisher und Subscriber über das Subscription-Protokoll verbunden. Subscription ist ein Verbindungsobjekt, das den Datenfluss steuert. Entwickler müssen keine eigenen Publisher-Typen erstellen — Apple stellt integrierte Typen bereit: Just, Future, Deferred, Fail, Empty sowie Publisher für URLSession und Timer.
Subject ist ein Publisher, in den manuell Werte gesendet werden können. PassthroughSubject speichert keinen Zustand: Ein Abonnent erhält nur Ereignisse, die nach dem Abonnement aufgetreten sind. CurrentValueSubject, ähnlich wie BehaviorRelay in RxSwift, speichert den aktuellen Wert und überträgt ihn sofort an neue Abonnenten.
Der Backpressure-Mechanismus in Combine reguliert die Datenübertragungsgeschwindigkeit. Der Subscriber teilt dem Publisher über subscription.request(.unlimited) oder subscription.request(.max(N)) mit, wie viele Werte er zu verarbeiten bereit ist. Dies verhindert einen Pufferüberlauf bei ungleichen Geschwindigkeiten von Produzent und Konsument.
Combine enthält mehr als 100 Operatoren, unterteilt in Kategorien: Transformation (map, tryMap, flatMap), Filterung (filter, compactMap, removeDuplicates), Kombination (combineLatest, merge, zip), Zeitverwaltung (debounce, throttle, delay) und Fehlerbehandlung (catch, replaceError, retry).
Der combineLatest-Operator kombiniert die letzten Werte zweier Publisher — jedes Mal, wenn einer einen neuen Wert emittiert, wird der Closure mit beiden aktualisierten Werten aufgerufen. Dieser Operator ist für die Formularvalidierung unverzichtbar, bei der der Status mehrerer Eingabefelder gleichzeitig verfolgt werden muss.
Der debounce-Operator verzögert die Veröffentlichung von Werten, bis ein bestimmtes Intervall ohne neue Ereignisse vergangen ist. Dies ist entscheidend für Suchfelder: Die Serveranfrage wird erst gesendet, nachdem der Benutzer für 300–500 ms mit der Eingabe aufgehört hat, was die Last um das 5- bis 10-fache reduziert.
Combine bietet mehrere Strategien zur Fehlerbehandlung auf Operatorenebene. catch fängt Failure ab und ersetzt den Publisher durch einen Fallback — der Stream läuft ohne Absturz weiter. replaceError(with:) stellt einen Standardwert anstelle des Fehlers bereit. retry(_:) wiederholt das Abonnement bei Fehlern eine bestimmte Anzahl von Malen. Wichtig: Ein Never-Fehlertyp in der Publisher-Signatur garantiert, dass der Stream nie mit einem Fehler endet — dies ermöglicht die Verwendung von Publisher in SwiftUI ohne Failure-Behandlung.
Das erste Beispiel zeigt die grundlegende Verwendung von Just — einem Publisher, der einen einzelnen Wert emittiert und abschließt:
let publisher = Just("Hello Combine")
publisher
.sink(receiveCompletion: { completion in
print("Abgeschlossen: \(completion)")
}, receiveValue: { value in
print("Erhalten: \(value)")
})
Das zweite Beispiel demonstriert die Integration von Combine mit URLSession für eine Netzwerkanfrage mit Ergebnisverarbeitung im Hauptthread:
let url = URL(string: "https://api.example.com")!
URLSession.shared
.dataTaskPublisher(for: url)
.map { $0.data }
.receive(on: DispatchQueue.main)
.sink(receiveCompletion: { completion in
if case .failure(let error) = completion {
print("Fehler: \(error)")
}
}, receiveValue: { data in
print("Empfangene Daten: \(data.count)")
})
Das dritte Beispiel zeigt die Verwendung von CurrentValueSubject für die Zustandsverwaltung:
let counter = CurrentValueSubject<Int, Never>(0)
counter
.sink { value in
print("Zähler: \(value)")
}
counter.send(1) // Gibt aus: Zähler: 1
counter.send(2) // Gibt aus: Zähler: 2
print(counter.value) // Gibt aus: 2
Combine und RxSwift lösen dasselbe Problem — reaktive Programmierung — jedoch mit unterschiedlichen Ansätzen. Combine ist ein natives Apple-Framework, das in das System integriert ist: keine Abhängigkeitsinstallation erforderlich, integriert mit SwiftUI und Foundation. RxSwift ist eine Drittanbieterbibliothek mit Unterstützung für iOS 9+ und einem breiteren Satz von Operatoren.
Die Leistung von Combine ist in typischen Szenarien dank nativer C-Implementierung höher. RxSwift bietet mehr Debugging-Tools (RxSwift.Resources.total, Debug), aber seine objektorientierte Architektur kann bei großen Datenströmen weniger effizient sein. Die Wahl zwischen ihnen ist ein Kompromiss zwischen Fähigkeiten und Integration.
Ein wichtiger Unterschied ist das Backpressure-Modell. In Combine verwaltet der Subscriber den Demand über Subscription und gibt so explizite Kontrolle über die Stream-Geschwindigkeit. In RxSwift wird Backpressure standardmäßig nicht kontrolliert — Observable emittiert alle Werte, und Subscriber verarbeitet sie, wenn sie eintreffen. Für große Datenströme ist Combine vorhersehbarer und speichersicherer.
Combine ist tief in SwiftUI auf Sprachebene integriert. @Published erzeugt einen Publisher, ObservableObject stellt objectWillChange Publisher bereit. SwiftUI View abonniert automatisch diese Publisher und zeichnet bei Änderungen neu. Für UIKit gibt es Drittanbieter-Wrapper wie CombineCocoa, aber Combine wurde ursprünglich für SwiftUI entwickelt, daher erfordert seine Integration in UIKit zusätzlichen Code. RxSwift hingegen hat ein reichhaltiges Ökosystem für UIKit: RxCocoa bietet reaktive Wrapper für alle UI-Elemente.
Combine unterstützt die Integration mit Core Data über NSFetchedResultsController Publisher. Der Publisher emittiert bei jeder Änderung des Core Data-Stacks neue Daten: Hinzufügen, Löschen, Aktualisieren von Datensätzen. Dies ermöglicht den Aufbau reaktiver Listen ohne manuelles reloadData. Durch die Kombination von Combine mit NotificationCenter Publisher für UIApplication.willResignActiveNotification erstellen Entwickler eine reaktive Schicht für den gesamten Anwendungslebenszyklus ohne Delegaten und Callbacks. Für ObservableObject-Klassen aktualisiert SwiftUI automatisch die View, wenn sich @Published-Eigenschaften ändern — Combine handhabt die gesamte Kette ohne eine einzige Zeile manuellen Codes. Unit-Tests von Combine-Code werden durch XCTestExpectation und Publisher.sink in Testfällen mit Scheduler-Isolation über ImmediateScheduler durchgeführt.
Bei der Arbeit mit Combine in UIKit-Projekten abonnieren Entwickler oft über sink und speichern AnyCancellable in einem Set. Typisches Muster: ViewController erstellt eine View, speichert cancellables, abonniert Publisher von ViewModel. Bei deinit werden alle Abonnements automatisch über die Cancellables-Sammlung gekündigt. Für die korrekte Handhabung von UIControl in Combine kann UIControl.Event Publisher über die publisher(for:)-Erweiterung verwendet werden.
Das Debugging von Combine erfolgt über den print()- oder handleEvents()-Operator. print(String) protokolliert alle Ereignisse: receive subscription, request demand, receive value, receive completion. handleEvents bietet feinere Kontrolle: Sie können Closures für jede Phase des Publisher-Lebenszyklus angeben. Zum Testen gibt es CombineExpectations — eine Bibliothek, die überprüft, ob ein Publisher in Tests mit virtueller Zeit und Demand-Steuerung die erwarteten Werte emittiert.
Combine wird aktiv in SwiftUI für die Formularverwaltung verwendet: Jede @Published-Eigenschaft erzeugt einen Publisher, SwiftUI abonniert ihn über View.body. Durch die Kombination mehrerer Publisher über combineLatest wird die Formularvalidierung deklarativ: Jeder Publisher verfolgt ein Feld, combineLatest sammelt alle Werte, map validiert sie und gibt den Status des Submit-Buttons zurück. Publishers.Merge kombiniert mehrere Publisher desselben Typs zu einem — nützlich zum Sammeln von Ereignissen verschiedener Steuerelemente in einem einzigen Stream.
Für die Combine-Integration mit UIKit verwenden Sie UIControl Publisher über eine Erweiterung: button.publisher(for: .touchUpInside) gibt einen Publisher zurück, der bei Tippen ein Ereignis emittiert. CombineCocoa ist eine Community-Bibliothek, die Publisher für alle UIControl-Ereignisse, UITextView.text und UIScrollView.contentOffset bereitstellt. Bei Kündigung wird der cancellables-Store in deinit geleert, wodurch garantiert wird, dass kein Abonnement seinen Besitzer überlebt. Für UIControl mit mehreren Zuständen verwenden Sie Publishers.MergeMany, um Publisher in einem Array zu kombinieren.
Häufig gestellte Fragen
Combine ist ab iOS 13, macOS 10.15, tvOS 13 und watchOS 6 verfügbar. Dies schränkt seine Verwendung in Projekten ein, die Unterstützung für ältere Versionen erfordern. Für iOS 12 und darunter wird RxSwift oder Drittanbieterbibliotheken verwendet.
PassthroughSubject speichert keinen Zustand und wiederholt den letzten Wert nicht für neue Abonnenten — sie erhalten nur zukünftige Ereignisse. CurrentValueSubject speichert den aktuellen Wert und überträgt ihn sofort an neue Abonnenten. CurrentValueSubject.value ermöglicht das synchrone Lesen und Schreiben des aktuellen Werts.
Combine ist der Kern von SwiftUI: @Published erzeugt einen Publisher, ObservableObject verwendet objectWillChange Publisher. SwiftUI View abonniert automatisch @ObservedObject und @StateObject über die Combine-Pipeline und zeichnet die View neu, wenn sich Eigenschaften ändern.
Backpressure ist ein Mechanismus zur Steuerung der Datenübertragungsgeschwindigkeit. Der Subscriber teilt der Subscription mit, wie viele Werte er zu verarbeiten bereit ist (Demand). Der Publisher kann nicht mehr als angefordert emittieren. Dies ist bei der Arbeit mit großen Datenströmen von Netzwerk-Sockets entscheidend.
Der catch-Operator fängt einen Fehler ab und ersetzt den Publisher durch einen Fallback. replaceError ersetzt den Fehler durch einen Standardwert. retry wiederholt das Abonnement bei Fehlern eine bestimmte Anzahl von Malen. Alle Operatoren sollten vor subscribe platziert werden, um Abstürze bei Failure zu verhindern.
Zusammenfassung
Wir entwickeln eine mobile Applikation schlüsselfertig
IT Sectr entwickelt seit 2017 iOS- und Android-Apps für Startups und Unternehmen. Wir beraten Sie und schlagen die beste Lösung vor.
Lesen Sie auch