Reaktive Programmierung in Swift: Combine und RxSwift
\n\nReaktive Programmierung ist ein Paradigma, das auf asynchrone Datenströme und die Verbreitung von Änderungen ausgerichtet ist. Im Swift-Ökosystem (iOS/macOS) sind die wichtigsten Werkzeuge zur Umsetzung dieses Paradigmas das Framework Combine (integriert in das Apple SDK) und die externe Bibliothek RxSwift. Beide Werkzeuge ermöglichen es, die Ereignisverarbeitung deklarativ zu beschreiben, die Anzahl der Callback-Funktionen zu reduzieren und den Code lesbarer und vorhersagbarer zu machen.
\n\nIn diesem Artikel betrachten wir die wichtigsten Konzepte der reaktiven Programmierung, vergleichen Combine und RxSwift und geben praktische Beispiele für iOS und macOS.
\n\nGrundlegende Konzepte
\nDie Grundlage des reaktiven Ansatzes bilden drei Entitäten:
\n\n- \n
- Publisher (Observable) — eine Datenquelle, die Ereignisse generiert (Werte, Fehler, Abschluss). \n
- Subscriber (Observer) — ein Konsument, der auf Ereignisse reagiert. \n
- Operator — eine Funktion, die den Datenstrom transformiert (Filterung, Transformation, Kombination). \n
Beispiel eines einfachen Publishers in Combine:
\nimport Combine\n\nlet publisher = Just("Hello, Reactive World!")\nlet subscription = publisher.sink { value in\n print(value)\n}\n// Ausgabe: Hello, Reactive World!\n\nCombine vs RxSwift: Vergleich
\nBeide Frameworks lösen ähnliche Aufgaben, haben aber Unterschiede:
\n\n- \n
- Combine — natives Apple-Framework (iOS 13+, macOS 10.15+). Verwendet strenge Typisierung und integriert sich mit SwiftUI und URLSession. \n
- RxSwift — plattformübergreifende Bibliothek mit einer großen Community. Unterstützt ältere iOS-Versionen (ab iOS 8). \n
- Leistung: Combine ist für Apple Silicon optimiert, RxSwift kann in benutzerdefinierten Szenarien flexibler sein. \n
Codebeispiele für iOS/macOS
\n\n1. Reaktive Verarbeitung eines Textfeldes (Combine + UIKit)
\nWir abonnieren Änderungen des Textes in einem UITextField:
\nimport 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("Benutzer hat eingegeben: \\(text)")\n // Hier kann eine Netzwerkanfrage gestellt oder die UI aktualisiert werden\n }\n .store(in: &cancellables)\n }\n}\n\n2. Kombinieren mehrerer Streams (RxSwift für macOS)
\nBeispiel für die Zusammenführung zweier observable-Sequenzen:
\nimport 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// Ausgabe:\n// 1 -> A\n// 2 -> B\n// 3 -> C\n\n3. Verarbeitung einer Netzwerkanfrage mit Combine
\nWir verwenden URLSession zum Laden von Daten:
\nimport 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