Programmation réactive en Swift : Combine et RxSwift pour iOS/macOS

Formateur Python en ligne pour débutants

Apprenez Python facilement sans surcharge théorique. Résolvez des tâches pratiques avec vérification automatique et écrivez du code directement dans le navigateur.

Start Course

Programmation réactive en Swift : Combine et RxSwift

\n\n

La programmation réactive est un paradigme orienté vers les flux de données asynchrones et la propagation des changements. Dans l'écosystème Swift (iOS/macOS), les principaux outils pour implémenter ce paradigme sont le framework Combine (intégré dans le SDK Apple) et la bibliothèque tierce RxSwift. Ces deux outils permettent de décrire de manière déclarative le traitement des événements, réduisant le nombre de fonctions de callback et rendant le code plus lisible et prévisible.

\n\n

Dans cet article, nous examinerons les concepts clés de la programmation réactive, comparerons Combine et RxSwift, et fournirons des exemples pratiques pour iOS et macOS.

\n\n

Concepts fondamentaux

\n

À la base de l'approche réactive se trouvent trois entités :

\n\n
    \n
  • Publisher (Observable) — une source de données qui génère des événements (valeurs, erreurs, achèvement).
  • \n
  • Subscriber (Observer) — un consommateur qui réagit aux événements.
  • \n
  • Operator — une fonction qui transforme le flux de données (filtrage, transformation, combinaison).
  • \n
\n\n

Exemple d'un Publisher simple dans Combine :

\n
import Combine\n\nlet publisher = Just("Bonjour, Monde Réactif !")\nlet subscription = publisher.sink { value in\n    print(value)\n}\n// Sortie : Bonjour, Monde Réactif !
\n\n

Combine vs RxSwift : comparaison

\n

Les deux frameworks résolvent des problèmes similaires, mais présentent des différences :

\n\n
    \n
  • Combine — framework natif d'Apple (iOS 13+, macOS 10.15+). Utilise un typage strict et s'intègre avec SwiftUI et URLSession.
  • \n
  • RxSwift — bibliothèque multiplateforme avec une large communauté. Prend en charge les versions plus anciennes d'iOS (à partir d'iOS 8).
  • \n
  • Performances : Combine est optimisé pour Apple Silicon, RxSwift peut être plus flexible dans des scénarios personnalisés.
  • \n
\n\n

Exemples de code pour iOS/macOS

\n\n

1. Traitement réactif d'un champ de texte (Combine + UIKit)

\n

Abonnement aux modifications du texte dans un UITextField :

\n
import UIKit\nimport Combine\n\nclass ViewController: UIViewController {\n    @IBOutlet weak var textField: UITextField!\n    private var cancellables = Set<AnyCancellable>()\n    \n    override func viewDidLoad() {\n        super.viewDidLoad()\n        \n        NotificationCenter.default.publisher(for: UITextField.textDidChangeNotification, object: textField)\n            .compactMap { ($0.object as? UITextField)?.text }\n            .debounce(for: .milliseconds(500), scheduler: RunLoop.main)\n            .sink { text in\n                print("L'utilisateur a saisi : \\(text)")\n                // Ici, on peut effectuer une requête réseau ou mettre à jour l'interface\n            }\n            .store(in: &cancellables)\n    }\n}
\n\n

2. Combinaison de plusieurs flux (RxSwift pour macOS)

\n

Exemple de combinaison de deux séquences observables :

\n
import RxSwift\n\nlet disposeBag = DisposeBag()\n\nlet firstObservable = Observable.of(1, 2, 3)\nlet secondObservable = Observable.of("A", "B", "C")\n\nObservable.zip(firstObservable, secondObservable)\n    .subscribe(onNext: { (number, letter) in\n        print("\\(number) -> \\(letter)")\n    })\n    .disposed(by: disposeBag)\n\n// Sortie :\n// 1 -> A\n// 2 -> B\n// 3 -> C
\n\n

3. Traitement d'une requête réseau avec Combine

\n

Utilisation d'URLSession pour charger des données :

\n
import Combine\nimport Foundation\n\nstruct Post: Decodable {\n    let id: Int\n    let title: String\n}\n\nfunc fetchPosts() -> AnyPublisher<[Post], Error> {\n    let url = URL(string: "https://jsonplaceholder.typicode.com/posts")!\n    return URLSession.shared.dataTaskPublisher(for: url)\n        .map(\\.data)\n        .decode(type: [Post].self, decoder: JSONDecoder())\n        .eraseToAnyPublisher()\n}\n                    

Recommendations