Combine: cos’è, concetti chiave e Publisher con Subscriber

Autore: IT Sectr Pubblicato: 2026-03-17 Tempo di lettura: 8 min

Combine è un framework di programmazione reattiva di Apple, presentato con iOS 13. Secondo Apple Documentation, 2025, Combine fornisce un’API dichiarativa unificata per la gestione di eventi asincroni. Publisher definisce la fonte dei dati, e Subscriber si sottoscrive per riceverli.

Punti chiave

  • Combine — un framework integrato Apple per la programmazione reattiva, disponibile da iOS 13
  • Publisher — un protocollo che definisce una fonte di valori Output con possibile errore Failure
  • Subscriber — un protocollo che riceve valori dal Publisher con gestione della demand
  • Subject — un Publisher che consente l’inserimento manuale di eventi (PassthroughSubject, CurrentValueSubject)
  • Operators — map, filter, combineLatest, flatMap per trasformare e combinare flussi

Cos’è Combine?

Combine è un framework dichiarativo Swift per elaborare eventi asincroni nel tempo. A differenza degli approcci basati su callback, Combine consente di descrivere pipeline di elaborazione dati attraverso una catena di operatori. Il framework è integrato con Foundation (URLSession, Timer, NotificationCenter) e SwiftUI (ObservableObject, @Published).

Protocollo Publisher

Qualsiasi tipo che implementa il protocollo Publisher deve definire Output (tipo di valori) e Failure (tipo di errore). Un Publisher non è attivo fino alla sottoscrizione — inizia a emettere eventi solo dopo la chiamata di subscribe. Questa è una semantica fredda, caratteristica dei flussi reattivi, che migliora l’efficienza.

Protocollo Subscriber

Subscriber riceve eventi attraverso tre metodi: receive(subscription:) — conferma della sottoscrizione con gestione della demand; receive(_:) — ricezione di un nuovo valore; receive(completion:) — completamento del flusso con successo o errore. Demand specifica quanti valori il Subscriber è pronto ad accettare — questo è il meccanismo di backpressure.

Publisher integrati

Apple fornisce molti Publisher integrati: Just emette un singolo valore e termina, Future — un risultato asincrono con una closure, Deferred — creazione differita di un Publisher fino alla sottoscrizione. URLSession.dataTaskPublisher converte una richiesta di rete in un flusso reattivo. Timer.publish crea un timer periodico. NotificationCenter.default.publisher trasforma le notifiche in un Publisher. Queste integrazioni consentono di sottoscriversi agli eventi senza ponte manuale.

Publisher e Subscriber in Combine

In Combine, Publisher e Subscriber sono collegati tramite il protocollo Subscription. Subscription è un oggetto di connessione che controlla il flusso di dati. Gli sviluppatori non devono creare propri tipi Publisher — Apple fornisce tipi integrati: Just, Future, Deferred, Fail, Empty, oltre a Publisher per URLSession e Timer.

Subject in Combine

Subject è un Publisher in cui è possibile inviare manualmente valori. PassthroughSubject non memorizza stato: un sottoscrittore riceve solo gli eventi avvenuti dopo la sottoscrizione. CurrentValueSubject, simile a BehaviorRelay in RxSwift, memorizza il valore corrente e lo trasmette immediatamente ai nuovi sottoscrittori.

Backpressure

Il meccanismo di backpressure in Combine regola la velocità di trasferimento dei dati. Il Subscriber comunica al Publisher tramite subscription.request(.unlimited) o subscription.request(.max(N)) quanti valori è pronto a elaborare. Ciò impedisce l’overflow del buffer quando le velocità di produttore e consumatore sono diseguali.

Operatori di Combine

Combine include più di 100 operatori, suddivisi in categorie: trasformazione (map, tryMap, flatMap), filtraggio (filter, compactMap, removeDuplicates), combinazione (combineLatest, merge, zip), gestione del tempo (debounce, throttle, delay) e gestione degli errori (catch, replaceError, retry).

L’operatore combineLatest combina gli ultimi valori di due Publisher — ogni volta che uno dei due emette un nuovo valore, la closure viene chiamata con entrambi i valori aggiornati. Questo operatore è indispensabile per la validazione di moduli dove è necessario tracciare lo stato di più campi di input simultaneamente.

L’operatore debounce ritarda la pubblicazione dei valori fino a quando non trascorre un intervallo specificato senza nuovi eventi. Questo è fondamentale per i campi di ricerca: la richiesta al server viene inviata solo dopo che l’utente smette di digitare per 300–500 ms, riducendo il carico di 5–10 volte.

Gestione degli errori

Combine offre diverse strategie di gestione degli errori a livello di operatori. catch intercetta Failure e sostituisce il Publisher con uno di riserva — il flusso continua senza crash. replaceError(with:) fornisce un valore predefinito invece dell’errore. retry(_:) riprova la sottoscrizione un numero specificato di volte in caso di fallimento. Importante: un tipo di errore Never nella firma del Publisher garantisce che il flusso non terminerà mai con un errore — ciò consente di utilizzare Publisher in SwiftUI senza gestione di Failure.

Esempi di codice Combine

Il primo esempio mostra l’uso base di Just — un Publisher che emette un singolo valore e termina:

swift
let publisher = Just("Hello Combine")

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

Il secondo esempio dimostra l’integrazione di Combine con URLSession per una richiesta di rete con elaborazione del risultato sul thread principale:

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("Errore: \(error)")
        }
    }, receiveValue: { data in
        print("Dati ricevuti: \(data.count)")
    })

Il terzo esempio mostra l’uso di CurrentValueSubject per la gestione dello stato:

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

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

counter.send(1) // Stampa: Contatore: 1
counter.send(2) // Stampa: Contatore: 2
print(counter.value) // Stampa: 2

Combine vs RxSwift

Combine e RxSwift risolvono lo stesso problema — la programmazione reattiva — ma con approcci diversi. Combine è un framework nativo Apple integrato nel sistema: nessuna installazione di dipendenze richiesta, integrato con SwiftUI e Foundation. RxSwift è una libreria di terze parti con supporto per iOS 9+ e un insieme più ampio di operatori.

Le prestazioni di Combine sono superiori negli scenari tipici grazie all’implementazione nativa in C. RxSwift fornisce più strumenti di debug (RxSwift.Resources.total, Debug), ma la sua architettura orientata agli oggetti può essere meno efficiente su grandi flussi di dati. La scelta tra loro è un compromesso tra capacità e integrazione.

Una differenza importante è il modello di backpressure. In Combine, il Subscriber gestisce la demand tramite Subscription, dando controllo esplicito sulla velocità del flusso. In RxSwift, la backpressure non è controllata per impostazione predefinita — Observable emette tutti i valori e Subscriber li elabora man mano che arrivano. Per grandi flussi di dati, Combine è più prevedibile e sicuro per la memoria.

Combine è profondamente integrato con SwiftUI a livello di linguaggio. @Published genera un Publisher, ObservableObject fornisce objectWillChange Publisher. SwiftUI View si sottoscrive automaticamente a questi Publisher e si ridisegna ai cambiamenti. Per UIKit esistono wrapper di terze parti come CombineCocoa, ma Combine è stato progettato originariamente per SwiftUI, quindi la sua integrazione in UIKit richiede codice aggiuntivo. RxSwift, d’altra parte, ha un ricco ecosistema per UIKit: RxCocoa fornisce wrapper reattivi per tutti gli elementi dell’interfaccia.

Combine supporta l’integrazione con Core Data tramite NSFetchedResultsController Publisher. Il Publisher emette nuovi dati a ogni modifica dello stack Core Data: aggiunta, eliminazione, aggiornamento di record. Ciò consente di creare elenchi reattivi senza chiamare manualmente reloadData. Combinando Combine con NotificationCenter Publisher per UIApplication.willResignActiveNotification, gli sviluppatori creano un livello reattivo per l’intero ciclo di vita dell’applicazione senza delegati e callback. Per le classi ObservableObject, SwiftUI aggiorna automaticamente la View quando le proprietà @Published cambiano — Combine gestisce l’intera catena senza una singola riga di codice manuale. I test unitari del codice Combine vengono eseguiti tramite XCTestExpectation e Publisher.sink in casi di test con isolamento dello Scheduler tramite ImmediateScheduler.

Quando si lavora con Combine in progetti UIKit, gli sviluppatori spesso si sottoscrivono tramite sink e memorizzano AnyCancellable in un Set. Schema tipico: ViewController crea una vista, memorizza i cancellables, si sottoscrive al Publisher di ViewModel. In deinit, tutte le sottoscrizioni vengono automaticamente annullate tramite la collezione Cancellables. Per la corretta gestione di UIControl in Combine, è possibile utilizzare UIControl.Event Publisher tramite l’estensione publisher(for:).

Il debug di Combine viene effettuato tramite l’operatore print() o handleEvents(). print(String) registra tutti gli eventi: receive subscription, request demand, receive value, receive completion. handleEvents fornisce un controllo più preciso: è possibile specificare closure per ogni fase del ciclo di vita del Publisher. Per i test, esiste CombineExpectations — una libreria che permette di verificare che un Publisher emetta i valori attesi nei test con tempo virtuale e controllo della demand.

Combine in pratica

Combine è attivamente utilizzato in SwiftUI per la gestione dei form: ogni proprietà @Published genera un Publisher, SwiftUI si sottoscrive tramite View.body. Combinando più Publisher tramite combineLatest, la validazione del form diventa dichiarativa: ogni Publisher traccia un campo, combineLatest raccoglie tutti i valori, map li valida e restituisce lo stato del pulsante Submit. Publishers.Merge combina più Publisher dello stesso tipo in uno solo — utile per raccogliere eventi da diversi controlli in un unico flusso.

Per l’integrazione di Combine con UIKit, utilizzare UIControl Publisher tramite un’estensione: button.publisher(for: .touchUpInside) restituisce un Publisher che emette un evento al tocco. CombineCocoa è una libreria della community che fornisce Publisher per tutti gli eventi UIControl, UITextView.text, UIScrollView.contentOffset. Alla cancellazione, il negozio di cancellables viene pulito in deinit, garantendo che nessuna sottoscrizione sopravviva al suo proprietario. Per UIControl con più stati, utilizzare Publishers.MergeMany per combinare Publisher in un array.

Domande frequenti

Da quale versione di iOS è disponibile Combine?

Combine è disponibile da iOS 13, macOS 10.15, tvOS 13 e watchOS 6. Ciò limita il suo utilizzo in progetti che richiedono supporto per versioni precedenti. Per iOS 12 e versioni inferiori, si utilizza RxSwift o librerie di terze parti.

Qual è la differenza tra PassthroughSubject e CurrentValueSubject?

PassthroughSubject non memorizza stato e non ripete l’ultimo valore ai nuovi sottoscrittori — ricevono solo eventi futuri. CurrentValueSubject memorizza il valore corrente e lo trasmette immediatamente ai nuovi sottoscrittori. CurrentValueSubject.value consente di leggere e scrivere il valore corrente in modo sincrono.

Come si integra Combine con SwiftUI?

Combine è al centro di SwiftUI: @Published genera un Publisher, ObservableObject utilizza objectWillChange Publisher. SwiftUI View si sottoscrive automaticamente a @ObservedObject e @StateObject tramite la pipeline Combine, ridisegnando la View quando le proprietà cambiano.

Cos’è il backpressure in Combine?

Backpressure è un meccanismo per controllare la velocità di trasferimento dei dati. Il Subscriber comunica alla Subscription quanti valori è pronto a elaborare (demand). Il Publisher non può emettere più del richiesto. Questo è fondamentale quando si lavora con grandi flussi di dati da socket di rete.

Come gestire gli errori in Combine?

L’operatore catch intercetta un errore e sostituisce il Publisher con uno di riserva. replaceError sostituisce l’errore con un valore predefinito. retry riprova la sottoscrizione un numero specificato di volte in caso di errore. Tutti gli operatori devono essere posizionati prima di subscribe per evitare crash in caso di Failure.

Riepilogo

  • Combine — un framework integrato Apple per la programmazione reattiva da iOS 13
  • Publisher — un protocollo con Output e Failure, inizia a funzionare dopo la sottoscrizione
  • Subscriber — riceve valori tramite receive con supporto alla gestione della demand
  • Subject (PassthroughSubject, CurrentValueSubject) — Publisher con inserimento manuale di eventi
  • Operatori map, filter, combineLatest, debounce, catch — la base delle pipeline
  • Backpressure tramite demand previene l’overflow del buffer del Subscriber
  • Scegli Combine per nuovi progetti con iOS 13+, RxSwift per supporto versioni precedenti

Svilupperemo un'applicazione mobile chiavi in mano

IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.

Discuti il progetto

Leggi anche