Combine : qu’est-ce que c’est, concepts clés et Publisher avec Subscriber

Auteur : IT Sectr Publié le : 2026-03-17 Temps de lecture : 8 min

Combine est un framework de programmation réactive d’Apple, présenté avec iOS 13. Selon Apple Documentation, 2025, Combine fournit une API déclarative unifiée pour le traitement des événements asynchrones. Publisher définit la source de données, et Subscriber s’y abonne pour les recevoir.

Points clés

  • Combine — un framework intégré d’Apple pour la programmation réactive, disponible depuis iOS 13
  • Publisher — un protocole définissant une source de valeurs Output avec une possible erreur Failure
  • Subscriber — un protocole recevant les valeurs du Publisher avec gestion de la demand
  • Subject — un Publisher permettant l’injection manuelle d’événements (PassthroughSubject, CurrentValueSubject)
  • Operators — map, filter, combineLatest, flatMap pour transformer et combiner les flux

Qu’est-ce que Combine ?

Combine est un framework Swift déclaratif pour le traitement d’événements asynchrones dans le temps. Contrairement aux approches basées sur les callbacks, Combine permet de décrire des pipelines de traitement de données via une chaîne d’opérateurs. Le framework est intégré à Foundation (URLSession, Timer, NotificationCenter) et à SwiftUI (ObservableObject, @Published).

Protocole Publisher

Tout type implémentant le protocole Publisher doit définir Output (type de valeurs) et Failure (type d‘erreur). Un Publisher n’est pas actif tant qu’il n’est pas abonné — il commence à émettre des événements seulement après l’appel de subscribe. C’est une sémantique froide, caractéristique des flux réactifs, améliorant l’efficacité.

Protocole Subscriber

Subscriber reçoit des événements via trois méthodes : receive(subscription:) — confirmation de l’abonnement avec gestion de la demand ; receive(_:) — réception d’une nouvelle valeur ; receive(completion:) — terminaison du flux avec succès ou erreur. La Demand spécifie combien de valeurs le Subscriber est prêt à accepter — c’est le mécanisme de backpressure.

Publishers intégrés

Apple fournit de nombreux Publishers intégrés : Just émet une seule valeur et se termine, Future — un résultat asynchrone avec une closure, Deferred — création différée d’un Publisher jusqu’à l’abonnement. URLSession.dataTaskPublisher transforme une requête réseau en flux réactif. Timer.publish crée une minuterie périodique. NotificationCenter.default.publisher convertit les notifications en Publisher. Ces intégrations permettent de s’abonner aux événements sans pont manuel.

Publisher et Subscriber dans Combine

Dans Combine, Publisher et Subscriber sont connectés via le protocole Subscription. Subscription est un objet de connexion qui contrôle le flux de données. Les développeurs n’ont pas besoin de créer leurs propres types Publisher — Apple fournit des types intégrés : Just, Future, Deferred, Fail, Empty, ainsi que des Publishers pour URLSession et Timer.

Subject dans Combine

Subject est un Publisher dans lequel on peut envoyer manuellement des valeurs. PassthroughSubject ne stocke pas d’état : un abonné reçoit uniquement les événements survenus après l’abonnement. CurrentValueSubject, similaire à BehaviorRelay dans RxSwift, stocke la valeur actuelle et la transmet immédiatement aux nouveaux abonnés.

Backpressure

Le mécanisme de backpressure dans Combine régule la vitesse de transfert des données. Le Subscriber indique au Publisher via subscription.request(.unlimited) ou subscription.request(.max(N)) combien de valeurs il est prêt à traiter. Cela évite le débordement du tampon lorsque les vitesses du producteur et du consommateur sont inégales.

Opérateurs Combine

Combine comprend plus de 100 opérateurs, répartis en catégories : transformation (map, tryMap, flatMap), filtrage (filter, compactMap, removeDuplicates), combinaison (combineLatest, merge, zip), gestion du temps (debounce, throttle, delay) et gestion des erreurs (catch, replaceError, retry).

L’opérateur combineLatest combine les dernières valeurs de deux Publishers — chaque fois que l’un d’eux émet une nouvelle valeur, la closure est appelée avec les deux valeurs mises à jour. Cet opérateur est indispensable pour la validation de formulaires où l’on doit suivre l’état de plusieurs champs de saisie simultanément.

L’opérateur debounce retarde la publication des valeurs jusqu’à ce qu’un intervalle spécifié se soit écoulé sans nouvel événement. C’est crucial pour les champs de recherche : la requête serveur n’est envoyée qu’après que l’utilisateur a cessé de taper pendant 300à500 ms, réduisant la charge de 5 à 10 fois.

Gestion des erreurs

Combine propose plusieurs stratégies de gestion des erreurs au niveau des opérateurs. catch intercepte Failure et remplace le Publisher par un de secours — le flux continue sans planter. replaceError(with:) fournit une valeur par défaut au lieu de l’erreur. retry(_:) réessaie l’abonnement un nombre spécifié de fois en cas d’échec. Important : un type d’erreur Never dans la signature du Publisher garantit que le flux ne se terminera jamais par une erreur — cela permet d’utiliser Publisher dans SwiftUI sans gestion de Failure.

Exemples de code Combine

Le premier exemple montre l’utilisation de base de Just — un Publisher qui émet une seule valeur et se termine :

swift
let publisher = Just("Hello Combine")

publisher
    .sink(receiveCompletion: { completion in
        print("Terminé : \(completion)")
    }, receiveValue: { value in
        print("Reçu : \(value)")
    })

Le deuxième exemple démontre l’intégration de Combine avec URLSession pour une requête réseau avec traitement du résultat sur le thread 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("Erreur : \(error)")
        }
    }, receiveValue: { data in
        print("Données reçues : \(data.count)")
    })

Le troisième exemple montre l’utilisation de CurrentValueSubject pour la gestion d’état :

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

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

counter.send(1) // Affiche : Compteur : 1
counter.send(2) // Affiche : Compteur : 2
print(counter.value) // Affiche : 2

Combine vs RxSwift

Combine et RxSwift résolvent le même problème — la programmation réactive — mais avec des approches différentes. Combine est un framework natif Apple intégré au système : aucune installation de dépendance nécessaire, intégré à SwiftUI et Foundation. RxSwift est une bibliothèque tierce prenant en charge iOS 9+ et offrant un ensemble plus large d’opérateurs.

Les performances de Combine sont supérieures dans les scénarios typiques grâce à l’implémentation native en C. RxSwift fournit plus d’outils de débogage (RxSwift.Resources.total, Debug), mais son architecture orientée objet peut être moins efficace sur les grands flux de données. Le choix entre eux est un compromis entre capacités et intégration.

Une différence importante est le modèle de backpressure. Dans Combine, le Subscriber gère la demand via Subscription, donnant un contrôle explicite sur la vitesse du flux. Dans RxSwift, la backpressure n’est pas contrôlée par défaut — Observable émet toutes les valeurs, et Subscriber les traite au fur et à mesure. Pour les grands flux de données, Combine est plus prévisible et sûr en mémoire.

Combine est profondément intégré à SwiftUI au niveau du langage. @Published génère un Publisher, ObservableObject fournit objectWillChange Publisher. SwiftUI View s’abonne automatiquement à ces Publishers et se redessine lors des changements. Pour UIKit, il existe des wrappers tiers comme CombineCocoa, mais Combine a été conçu à l’origine pour SwiftUI, donc son intégration dans UIKit nécessite du code supplémentaire. RxSwift, en revanche, dispose d’un écosystème riche pour UIKit : RxCocoa fournit des wrappers réactifs pour tous les éléments d’interface.

Combine prend en charge l’intégration avec Core Data via NSFetchedResultsController Publisher. Le Publisher émet de nouvelles données à chaque modification de la pile Core Data : ajout, suppression, mise à jour d’enregistrements. Cela permet de construire des listes réactives sans appeler manuellement reloadData. En combinant Combine avec NotificationCenter Publisher pour UIApplication.willResignActiveNotification, les développeurs créent une couche réactive pour l’ensemble du cycle de vie de l’application sans délégues ni callbacks. Pour les classes ObservableObject, SwiftUI met automatiquement à jour la View lors des changements de propriétés @Published — Combine gère toute la chaîne sans une seule ligne de code manuel. Les tests unitaires du code Combine s’effectuent via XCTestExpectation et Publisher.sink dans des cas de test avec isolation du Scheduler via ImmediateScheduler.

Lorsqu’on travaille avec Combine dans des projets UIKit, les développeurs s’abonnent souvent via sink et stockent AnyCancellable dans un Set. Le motif typique : ViewController crée une vue, stocke les cancellables, s’abonne au Publisher du ViewModel. Dans deinit, tous les abonnements sont automatiquement annulés via la collection Cancellables. Pour la gestion correcte d’UIControl dans Combine, vous pouvez utiliser UIControl.Event Publisher via l’extension publisher(for:).

Le débogage de Combine s’effectue via l’opérateur print() ou handleEvents(). print(String) enregistre tous les événements : receive subscription, request demand, receive value, receive completion. handleEvents offre un contrôle plus fin : vous pouvez spécifier des closures pour chaque étape du cycle de vie du Publisher. Pour les tests, il existe CombineExpectations — une bibliothèque qui permet de vérifier qu’un Publisher émet les valeurs attendues dans des tests avec temps virtuel et contrôle de la demand.

Combine en pratique

Combine est activement utilisé dans SwiftUI pour la gestion de formulaires : chaque propriété @Published génère un Publisher, SwiftUI s’y abonne via View.body. En combinant plusieurs Publishers via combineLatest, la validation de formulaire devient déclarative : chaque Publisher suit un champ, combineLatest collecte toutes les valeurs, map les valide et renvoie l’état du bouton Submit. Publishers.Merge combine plusieurs Publishers du même type en un seul — utile pour collecter les événements de différents contrôles dans un flux unique.

Pour l’intégration de Combine avec UIKit, utilisez UIControl Publisher via une extension : button.publisher(for: .touchUpInside) renvoie un Publisher qui émet un événement lors d’un tap. CombineCocoa est une bibliothèque communautaire fournissant des Publishers pour tous les événements UIControl, UITextView.text, UIScrollView.contentOffset. Lors de la résiliation, le magasin de cancellables est vidé dans deinit, garantissant qu’aucun abonnement ne survive à son propriétaire. Pour UIControl avec plusieurs états, utilisez Publishers.MergeMany pour combiner des Publishers en un tableau.

Questions fréquentes

À partir de quelle version d’iOS Combine est-il disponible ?

Combine est disponible à partir d’iOS 13, macOS 10.15, tvOS 13 et watchOS 6. Cela limite son utilisation dans les projets nécessitant la prise en charge de versions plus anciennes. Pour iOS 12 et inférieur, RxSwift ou des bibliothèques tierces sont utilisés.

Quelle est la différence entre PassthroughSubject et CurrentValueSubject ?

PassthroughSubject ne stocke pas d’état et ne répète pas la dernière valeur aux nouveaux abonnés — ils ne reçoivent que les événements futurs. CurrentValueSubject stocke la valeur actuelle et la transmet immédiatement aux nouveaux abonnés. CurrentValueSubject.value permet de lire et d’écrire la valeur actuelle de manière synchrone.

Comment Combine s’intègre-t-il avec SwiftUI ?

Combine est au cœur de SwiftUI : @Published génère un Publisher, ObservableObject utilise objectWillChange Publisher. SwiftUI View s’abonne automatiquement à @ObservedObject et @StateObject via le pipeline Combine, redessinant la View lors des changements de propriétés.

Qu’est-ce que le backpressure dans Combine ?

Backpressure est un mécanisme de contrôle de la vitesse de transfert des données. Le Subscriber indique à la Subscription combien de valeurs il est prêt à traiter (demand). Le Publisher ne peut pas émettre plus que demandé. C’est crucial lors du travail avec de grands flux de données provenant de sockets réseau.

Comment gérer les erreurs dans Combine ?

L’opérateur catch intercepte une erreur et remplace le Publisher par un de secours. replaceError remplace l’erreur par une valeur par défaut. retry réessaie l’abonnement un nombre spécifié de fois en cas d’erreur. Tous les opérateurs doivent être placés avant subscribe pour éviter les plantages en cas de Failure.

Résumé

  • Combine — un framework intégré d’Apple pour la programmation réactive depuis iOS 13
  • Publisher — un protocole avec Output et Failure, commence à fonctionner après abonnement
  • Subscriber — reçoit des valeurs via receive avec prise en charge de la gestion de la demand
  • Subject (PassthroughSubject, CurrentValueSubject) — Publisher avec injection manuelle d’événements
  • Opérateurs map, filter, combineLatest, debounce, catch — la base des pipelines
  • Backpressure via la demand empêche le débordement du tampon du Subscriber
  • Choisissez Combine pour les nouveaux projets ciblant iOS 13+, RxSwift pour la prise en charge des versions plus anciennes

Nous développerons une application mobile clé en main

IT Sectr crée des applications iOS et Android pour les startups et les entreprises depuis 2017. Nous vous conseillerons et vous proposerons la meilleure solution.

Discuter du projet

Lisez aussi