Combine is een framework voor reactief programmeren van Apple, geïntroduceerd in iOS 13. Volgens Apple Documentation, 2025 biedt Combine een uniforme declaratieve API voor het verwerken van asynchrone gebeurtenissen. Publisher definieert de gegevensbron en Subscriber abonneert zich om ze te ontvangen.
Belangrijkste
Combine is een declaratief Swift-framework voor het verwerken van asynchrone gebeurtenissen in de tijd. In tegenstelling tot callback-gebaseerde benaderingen, maakt Combine het mogelijk om gegevensverwerkingspijplijnen te beschrijven via een keten van operatoren. Het framework is geïntegreerd met Foundation (URLSession, Timer, NotificationCenter) en SwiftUI (ObservableObject, @Published).
Elk type dat het Publisher-protocol implementeert, moet Output (type waarden) en Failure (type fout) definiëren. Publisher is niet actief tot abonnement — het begint pas gebeurtenissen te emitteren na het aanroepen van subscribe. Dit is koude semantiek, kenmerkend voor reactieve streams, die de efficiëntie verhoogt.
Subscriber ontvangt gebeurtenissen via drie methoden: receive(subscription:) — bevestiging van abonnement met demand-beheer; receive(_:) — ontvangst van een nieuwe waarde; receive(completion:) — voltooiing van de stream met succes of fout. Demand specificeert het aantal waarden dat Subscriber bereid is te ontvangen — dit is het backpressure-mechanisme.
Apple biedt vele ingebouwde Publishers: Just emitteert één waarde en eindigt, Future — asynchroon resultaat met closure, Deferred — uitgestelde creatie van Publisher tot het moment van abonnement. URLSession.dataTaskPublisher zet een netwerkverzoek om in een reactieve stream. Timer.publish creëert een periodieke timer. NotificationCenter.default.publisher zet meldingen om in een Publisher. Deze integraties maken het mogelijk om op gebeurtenissen te abonneren zonder handmatige brug.
In Combine zijn Publisher en Subscriber verbonden via het Subscription-protocol. Subscription is een verbindingsobject dat de gegevensstroom controleert. De ontwikkelaar hoeft geen eigen Publisher-typen te maken — Apple biedt ingebouwde: Just, Future, Deferred, Fail, Empty, evenals Publisher voor URLSession en Timer.
Subject is een Publisher waarin handmatig waarden kunnen worden verzonden. PassthroughSubject slaat geen status op: de abonnee ontvangt alleen gebeurtenissen die na het abonnement hebben plaatsgevonden. CurrentValueSubject, vergelijkbaar met BehaviorRelay in RxSwift, slaat de huidige waarde op en geeft deze onmiddellijk door aan een nieuwe abonnee.
Het backpressure-mechanisme in Combine regelt de snelheid van gegevensoverdracht. Subscriber informeert Publisher via subscription.request(.unlimited) of subscription.request(.max(N)) hoeveel waarden het klaar is om te verwerken. Dit voorkomt bufferoverloop bij ongelijke snelheid van producent en consument.
Combine bevat meer dan 100 operatoren verdeeld in categorieën: transformatie (map, tryMap, flatMap), filtering (filter, compactMap, removeDuplicates), combineren (combineLatest, merge, zip), tijdbeheer (debounce, throttle, delay) en foutafhandeling (catch, replaceError, retry).
De operator combineLatest combineert de laatste waarden van twee Publishers — elke keer wanneer een van hen een nieuwe waarde emitteert, wordt de closure aangeroepen met beide bijgewerkte waarden. Deze operator is onmisbaar bij formuliervalidatie waar de status van meerdere invoervelden tegelijkertijd moet worden gevolgd.
De operator debounce vertraagt de publicatie van waarden totdat een gespecificeerd interval zonder nieuwe gebeurtenissen is verstreken. Dit is cruciaal voor zoekvelden: het verzoek naar de server wordt pas verzonden nadat de gebruiker 300-500 ms is gestopt met typen, wat de belasting 5-10 keer vermindert.
Combine biedt verschillende strategieën voor foutafhandeling op operatorniveau. catch onderschept Failure en vervangt Publisher door een back-up — de stream gaat verder zonder crash. replaceError(with:) plaatst een standaardwaarde in plaats van de fout. retry(_:) herhaalt het abonnement een gespecificeerd aantal keren bij fout. Belangrijk: het fouttype Never in de handtekening van Publisher garandeert dat de stream nooit met een fout zal eindigen — dit maakt het mogelijk Publisher in SwiftUI te gebruiken zonder Failure-afhandeling.
Het eerste voorbeeld toont basiswerk met Just — Publisher die één waarde emitteert en eindigt:
let publisher = Just("Hello Combine")
publisher
.sink(receiveCompletion: { completion in
print("Voltooid: \(completion)")
}, receiveValue: { value in
print("Ontvangen: \(value)")
})
Het tweede voorbeeld demonstreert de integratie van Combine met URLSession voor een netwerkverzoek met verwerking van het resultaat op de hoofdthread:
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("Fout: \(error)")
}
}, receiveValue: { data in
print("Gegevens ontvangen: \(data.count)")
})
Het derde voorbeeld — gebruik van CurrentValueSubject voor statusbeheer:
let counter = CurrentValueSubject<Int, Never>(0)
counter
.sink { value in
print("Teller: \(value)")
}
counter.send(1) // Toont: Teller: 1
counter.send(2) // Toont: Teller: 2
print(counter.value) // Toont: 2
Combine en RxSwift lossen dezelfde taak op — reactief programmeren, maar met verschillende benaderingen. Combine is een native Apple-framework ingebouwd in het systeem: vereist geen installatie van afhankelijkheden, is geïntegreerd met SwiftUI en Foundation. RxSwift is een bibliotheek van derden met ondersteuning voor iOS 9+ en een bredere set operatoren.
De prestaties van Combine zijn hoger in typische scenario's dankzij native implementatie in C. RxSwift biedt meer tools voor debugging (RxSwift.Resources.total, Debug), maar de object-georiënteerde architectuur kan minder efficiënt zijn bij grote gegevensstromen. De keuze tussen hen is een compromis tussen mogelijkheden en integratie.
Een belangrijk verschil — het backpressure-model. In Combine beheert Subscriber demand via Subscription, wat expliciete controle over de stroomsnelheid geeft. In RxSwift wordt backpressure standaard niet gecontroleerd — Observable emitteert alle waarden en Subscriber verwerkt ze wanneer ze binnenkomen. Voor grote gegevensstromen blijkt Combine voorspelbaarder en geheugenvriendelijker.
Combine is diep geïntegreerd met SwiftUI op taalniveau. @Published genereert Publisher, ObservableObject biedt objectWillChange Publisher. SwiftUI View abonneert zich automatisch op deze Publishers en hertekent bij wijzigingen. Voor UIKit zijn er externe wrappers zoals CombineCocoa, maar Combine is oorspronkelijk ontworpen voor SwiftUI, dus de integratie in UIKit vereist extra code. RxSwift heeft daarentegen een rijke ecosysteem voor UIKit: RxCocoa biedt reactieve wrappers voor alle UI-elementen.
Combine ondersteunt integratie met Core Data via NSFetchedResultsController Publisher. Publisher emitteert nieuwe gegevens bij elke wijziging van de Core Data-stack: toevoegen, verwijderen, bijwerken van records. Dit maakt het mogelijk reactieve lijsten te bouwen zonder handmatig reloadData aan te roepen. Door Combine te combineren met NotificationCenter Publisher voor UIApplication.willResignActiveNotification creëert de ontwikkelaar een reactieve laag voor de hele levenscyclus van de applicatie zonder delegates en callbacks. Voor ObservableObject-klassen werkt SwiftUI View automatisch bij bij wijziging van @Published-eigenschappen — Combine zorgt voor de hele verbinding zonder een enkele regel handmatige code. Modulair testen van Combine-code gebeurt via XCTestExpectation en Publisher.sink in testcases met Scheduler-isolatie via ImmediateScheduler.
Bij het werken met Combine in UIKit-projecten gebruiken ontwikkelaars vaak abonnement via sink en slaan AnyCancellable op in Set. Het typische patroon: ViewController maakt een view aan, slaat cancellables op, abonneert zich op Publisher uit ViewModel. Bij deinit worden alle abonnementen automatisch geannuleerd via de Cancellables-collectie. Voor correct werken met UIControl in Combine kan UIControl.Event Publisher worden gebruikt via de publisher(for:)-extensie.
Debugging van Combine gebeurt via de operator print() of handleEvents(). print(String) logt alle gebeurtenissen: receive subscription, request demand, receive value, receive completion. handleEvents geeft fijnere controle: er kunnen closures worden gespecificeerd voor elke fase van de levenscyclus van Publisher. Voor het testen van Combine bestaat CombineExpectations — een bibliotheek die controleert of Publisher de verwachte waarden emitteert in tests met virtuele tijd en demand-controle.
Combine wordt actief gebruikt in SwiftUI voor formulierbeheer: elke @Published-eigenschap genereert een Publisher, SwiftUI abonneert zich erop via View.body. Door meerdere Publishers te combineren via combineLatest wordt formuliervalidatie declaratief: elke Publisher volgt één veld, combineLatest verzamelt alle waarden, map controleert ze en retourneert de status van de Submit-knop. Publishers.Merge combineert meerdere Publishers van hetzelfde type tot één — handig voor het verzamelen van gebeurtenissen van verschillende bedieningselementen in één stroom.
Voor integratie van Combine met UIKit gebruikt u UIControl Publisher via extensie: button.publisher(for: .touchUpInside) retourneert een Publisher die bij indrukken een gebeurtenis emitteert. CombineCocoa — een communitybibliotheek die Publisher biedt voor alle UIControl-gebeurtenissen, UITextView.text, UIScrollView.contentOffset. Bij opzegging van abonnement wordt cancellables Store in deinit opgeschoond, wat garandeert dat geen abonnement zijn eigenaar overleeft. Voor UIControl met meerdere statussen gebruikt u Publishers.MergeMany om Publishers in een array te combineren.
Veelgestelde vragen
Combine is beschikbaar vanaf iOS 13, macOS 10.15, tvOS 13 en watchOS 6. Dit beperkt het gebruik in projecten die ondersteuning voor oudere versies vereisen. Voor iOS 12 en lager wordt RxSwift of bibliotheken van derden gebruikt.
PassthroughSubject slaat geen status op en herhaalt de laatste waarde niet voor nieuwe abonnees — zij ontvangen alleen toekomstige gebeurtenissen. CurrentValueSubject slaat de huidige waarde op en geeft deze onmiddellijk door aan een nieuwe abonnee. CurrentValueSubject.value maakt het mogelijk de huidige waarde synchroon te lezen en te schrijven.
Combine vormt de basis van SwiftUI: @Published genereert Publisher, ObservableObject gebruikt objectWillChange Publisher. SwiftUI View abonneert zich automatisch op @ObservedObject en @StateObject via de Combine-pijplijn en hertekent de View bij wijziging van eigenschappen.
Backpressure — een mechanisme voor het beheren van de gegevensoverdrachtsnelheid. Subscriber informeert Subscription hoeveel waarden het klaar is om te verwerken (demand). Publisher kan niet meer emitteren dan gevraagd. Dit is cruciaal bij het werken met grote gegevensstromen uit netwerksockets.
De operator catch onderschept de fout en vervangt Publisher door een fallback. replaceError vervangt de fout door een standaardwaarde. retry herhaalt het abonnement een gespecificeerd aantal keren bij fout. Alle operatoren moeten vóór subscribe worden geplaatst om een crash bij Failure te voorkomen.
Samenvatting
We ontwikkelen een mobiele applicatie turnkey
IT Sectr creëert sinds 2017 iOS- en Android-applicaties voor startups en bedrijven. We adviseren u en stellen de beste oplossing voor.
Lees ook