Combine: ce este, concepte cheie și Publisher cu Subscriber

Autor: IT Sectr Publicat: 2026-03-17 Timp de citire: 8 min

Combine este un framework de programare reactivă de la Apple, introdus în iOS 13. Potrivit Apple Documentation, 2025, Combine oferă un API declarativ unificat pentru procesarea evenimentelor asincrone. Publisher definește sursa de date, iar Subscriber se abonează pentru a le primi.

Principalele

  • Combine — framework-ul Apple încorporat pentru programare reactivă, disponibil din iOS 13
  • Publisher — protocol care definește sursa valorilor Output cu posibilitatea de eroare Failure
  • Subscriber — protocol care primește valori de la Publisher cu demand gestionat
  • Subject — Publisher care permite inserarea manuală a evenimentelor (PassthroughSubject, CurrentValueSubject)
  • Operators — map, filter, combineLatest, flatMap pentru transformarea și combinarea fluxurilor

Ce este Combine?

Combine este un framework declarativ Swift pentru procesarea evenimentelor asincrone în timp. Spre deosebire de abordările bazate pe callback, Combine permite descrierea conductelor de procesare a datelor printr-un lanț de operatori. Framework-ul este integrat cu Foundation (URLSession, Timer, NotificationCenter) și SwiftUI (ObservableObject, @Published).

Protocolul Publisher

Orice tip care implementează protocolul Publisher trebuie să definească Output (tipul valorilor) și Failure (tipul erorii). Publisher nu este activ până la abonare — începe să emită evenimente doar după apelarea subscribe. Aceasta este semantica rece, caracteristică fluxurilor reactive, care crește eficiența.

Protocolul Subscriber

Subscriber primește evenimente prin trei metode: receive(subscription:) — confirmarea abonării cu gestionarea demand; receive(_:) — primirea unei noi valori; receive(completion:) — finalizarea fluxului cu succes sau eroare. Demand specifică numărul de valori pe care Subscriber este gata să le primească — acesta este mecanismul backpressure.

Publisheri încorporați

Apple oferă mulți Publisheri încorporați: Just emite o valoare și se termină, Future — rezultat asincron cu closure, Deferred — crearea întârziată a Publisher până la momentul abonării. URLSession.dataTaskPublisher transformă cererea de rețea într-un flux reactiv. Timer.publish creează un timer periodic. NotificationCenter.default.publisher transformă notificările în Publisher. Aceste integrări permit abonarea la evenimente fără punte manuală.

Publisher și Subscriber în Combine

În Combine Publisher și Subscriber sunt conectate prin protocolul Subscription. Subscription este un obiect de legătură care controlează fluxul de date. Dezvoltatorul nu trebuie să-și creeze propriile tipuri Publisher — Apple oferă Publisheri încorporați: Just, Future, Deferred, Fail, Empty, precum și Publisher pentru URLSession și Timer.

Subject în Combine

Subject este un Publisher în care se pot trimite manual valori. PassthroughSubject nu stochează starea: abonatul primește doar evenimente care au avut loc după abonare. CurrentValueSubject, similar cu BehaviorRelay din RxSwift, stochează valoarea curentă și o transmite imediat noului abonat.

Backpressure

Mecanismul backpressure în Combine reglează viteza de transmitere a datelor. Subscriber informează Publisher prin subscription.request(.unlimited) sau subscription.request(.max(N)) câte valori este gata să proceseze. Acest lucru previne debordarea bufferului la viteza inegală a producătorului și consumatorului.

Operatori Combine

Combine include peste 100 de operatori împărțiți pe categorii: transformare (map, tryMap, flatMap), filtrare (filter, compactMap, removeDuplicates), combinare (combineLatest, merge, zip), gestionarea timpului (debounce, throttle, delay) și gestionarea erorilor (catch, replaceError, retry).

Operatorul combineLatest combină ultimele valori a doi Publisher — de fiecare dată când oricare dintre ei emite o nouă valoare, closure-ul este apelat cu ambele valori actualizate. Acest operator este indispensabil la validarea formularelor unde trebuie urmărită starea mai multor câmpuri de intrare simultan.

Operatorul debounce întârzie publicarea valorilor până când trece un interval specificat fără evenimente noi. Acest lucru este critic pentru câmpurile de căutare: cererea către server este trimisă doar după ce utilizatorul a încetat să tasteze timp de 300-500 ms, reducând încărcarea de 5-10 ori.

Gestionarea erorilor

Combine oferă mai multe strategii de gestionare a erorilor la nivel de operatori. catch interceptează Failure și înlocuiește Publisher cu unul de rezervă — fluxul continuă fără crash. replaceError(with:) pune o valoare implicită în locul erorii. retry(_:) repetă abonarea de un număr specificat de ori la eșec. Important: tipul de eroare Never în semnătura Publisher garantează că fluxul nu se va termina niciodată cu eroare — acest lucru permite utilizarea Publisher în SwiftUI fără gestionarea Failure.

Exemple de cod Combine

Primul exemplu arată lucrul de bază cu Just — Publisher care emite o valoare și se termină:

swift
let publisher = Just("Hello Combine")

publisher
    .sink(receiveCompletion: { completion in
        print("Finalizat: \(completion)")
    }, receiveValue: { value in
        print("Primit: \(value)")
    })

Al doilea exemplu demonstrează integrarea Combine cu URLSession pentru o cerere de rețea cu procesarea rezultatului pe thread-ul principal:

swift
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("Eroare: \(error)")
        }
    }, receiveValue: { data in
        print("Date primite: \(data.count)")
    })

Al treilea exemplu — utilizarea CurrentValueSubject pentru gestionarea stării:

swift
let counter = CurrentValueSubject<Int, Never>(0)

counter
    .sink { value in
        print("Contor: \(value)")
    }

counter.send(1) // Va afișa: Contor: 1
counter.send(2) // Va afișa: Contor: 2
print(counter.value) // Va afișa: 2

Combine vs RxSwift

Combine și RxSwift rezolvă aceeași sarcină — programarea reactivă, dar cu abordări diferite. Combine este un framework nativ Apple încorporat în sistem: nu necesită instalarea de dependențe, este integrat cu SwiftUI și Foundation. RxSwift este o bibliotecă terță cu suport pentru iOS 9+ și un set mai larg de operatori.

Performanța Combine este mai mare în scenariile tipice datorită implementării native în C. RxSwift oferă mai multe instrumente de depanare (RxSwift.Resources.total, Debug), dar arhitectura sa orientată pe obiecte poate fi mai puțin eficientă pe fluxuri mari de date. Alegerea între ele este un compromis între capacități și integrare.

O diferență importantă — modelul backpressure. În Combine Subscriber gestionează demand prin Subscription, ceea ce oferă un control explicit asupra vitezei fluxului. În RxSwift backpressure nu este controlat implicit — Observable emite toate valorile, iar Subscriber le procesează pe măsură ce sosesc. Pentru fluxuri mari de date Combine se dovedește mai predictibil și mai sigur din punct de vedere al memoriei.

Combine este profund integrat cu SwiftUI la nivel de limbaj. @Published generează Publisher, ObservableObject oferă Publisher objectWillChange. SwiftUI View se abonează automat la acești Publisher și se redesenează la modificări. Pentru UIKit există învelitori terțe precum CombineCocoa, dar Combine a fost proiectat inițial pentru SwiftUI, deci integrarea sa în UIKit necesită cod suplimentar. RxSwift, dimpotrivă, are un ecosistem bogat pentru UIKit: RxCocoa oferă învelitori reactive pentru toate elementele UI.

Combine suportă integrarea cu Core Data prin NSFetchedResultsController Publisher. Publisher emite date noi la fiecare modificare a stivei Core Data: adăugare, ștergere, actualizare de înregistrări. Acest lucru permite construirea de liste reactive fără apelarea manuală a reloadData. Combinând Combine cu NotificationCenter Publisher pentru UIApplication.willResignActiveNotification, dezvoltatorul creează un strat reactiv pentru întregul ciclu de viață al aplicației fără delegate și callback-uri. Pentru clasele ObservableObject, SwiftUI actualizează automat View la modificarea proprietăților @Published — Combine asigură întreaga legătură fără o singură linie de cod manual. Testarea modulară a codului Combine se realizează prin XCTestExpectation și Publisher.sink în cazuri de test cu izolarea Scheduler prin ImmediateScheduler.

La lucrul cu Combine în proiecte UIKit, dezvoltatorii folosesc adesea abonarea prin sink și stochează AnyCancellable în Set. Modelul tipic: ViewController creează o vedere, stochează cancellables, se abonează la Publisher din ViewModel. La deinit toate abonările sunt anulate automat prin colecția Cancellables. Pentru lucrul corect cu UIControl în Combine se poate folosi UIControl.Event Publisher prin extensia publisher(for:).

Depanarea Combine se realizează prin operatorul print() sau handleEvents(). print(String) înregistrează toate evenimentele: receive subscription, request demand, receive value, receive completion. handleEvents oferă un control mai fin: se pot specifica closure-uri pentru fiecare etapă a ciclului de viață al Publisher. Pentru testarea Combine există CombineExpectations — o bibliotecă care permite verificarea că Publisher emite valorile așteptate în teste cu timp virtual și controlul demand.

Combine în practică

Combine este utilizat activ în SwiftUI pentru gestionarea formularelor: fiecare proprietate @Published generează un Publisher, SwiftUI se abonează la el prin View.body. Combinând mai mulți Publisher prin combineLatest, validarea formularului devine declarativă: fiecare Publisher urmărește un câmp, combineLatest colectează toate valorile, map le verifică și returnează starea butonului Submit. Publishers.Merge combină mai mulți Publisher de același tip într-unul singur — util pentru colectarea evenimentelor de la diferite controale într-un singur flux.

Pentru integrarea Combine cu UIKit utilizați UIControl Publisher prin extensie: button.publisher(for: .touchUpInside) returnează un Publisher care emite un eveniment la apăsare. CombineCocoa — o bibliotecă din comunitate care oferă Publisher pentru toate evenimentele UIControl, UITextView.text, UIScrollView.contentOffset. La dezabonare, cancellables Store este curățat în deinit, garantând că niciun abonament nu va supraviețui proprietarului său. Pentru UIControl cu stări multiple utilizați Publishers.MergeMany pentru a combina Publisheri într-un array.

Întrebări frecvente

De la ce versiune iOS este disponibil Combine?

Combine este disponibil din iOS 13, macOS 10.15, tvOS 13 și watchOS 6. Acest lucru limitează utilizarea sa în proiecte care necesită suport pentru versiuni mai vechi. Pentru iOS 12 și mai jos se aplică RxSwift sau biblioteci terțe.

Cu ce se deosebește PassthroughSubject de CurrentValueSubject?

PassthroughSubject nu stochează starea și nu repetă ultima valoare noilor abonați — aceștia primesc doar evenimente viitoare. CurrentValueSubject stochează valoarea curentă și o transmite imediat noului abonat. CurrentValueSubject.value permite citirea și scrierea valorii curente sincron.

Cum se integrează Combine cu SwiftUI?

Combine stă la baza SwiftUI: @Published generează Publisher, ObservableObject folosește Publisher objectWillChange. SwiftUI View se abonează automat la @ObservedObject și @StateObject prin conducta Combine, redesenuând View la modificarea proprietăților.

Ce este backpressure în Combine?

Backpressure — mecanism de gestionare a vitezei de transmitere a datelor. Subscriber informează Subscription câte valori este gata să proceseze (demand). Publisher nu poate emite mai mult decât a fost solicitat. Acest lucru este critic la lucrul cu fluxuri mari de date din socket-uri de rețea.

Cum se gestionează erorile în Combine?

Operatorul catch interceptează eroarea și înlocuiește Publisher cu un fallback. replaceError înlocuiește eroarea cu o valoare implicită. retry repetă abonarea de un număr specificat de ori la eroare. Toți operatorii trebuie plasați înainte de subscribe pentru a exclude crash-ul la Failure.

Rezumat

  • Combine — framework-ul Apple încorporat pentru programare reactivă din iOS 13
  • Publisher — protocol cu Output și Failure, începe lucrul după abonare
  • Subscriber — primește valori prin receive cu suport demand
  • Subject (PassthroughSubject, CurrentValueSubject) — Publisher cu inserare manuală de evenimente
  • Operatori map, filter, combineLatest, debounce, catch — baza conductei
  • Backpressure prin demand previne debordarea bufferului Subscriber
  • Alegeți Combine pentru proiecte noi de la iOS 13, RxSwift — pentru suportul versiunilor vechi

Vom dezvolta o aplicație mobilă la cheie

IT Sectr creează aplicații iOS și Android pentru startup-uri și afaceri din 2017. Vă vom consilia și vă vom propune cea mai bună soluție.

Discutați proiectul

Citiți și