RxSwift – Grundlagen und praktische Beispiele

Seite 2: RxSwift – Umsetzung mit Swift

Inhaltsverzeichnis

RxSwift wurde 2015 in Version 1.0 veröffentlicht – drei Monate nach der Vorstellung von Swift selbst. Seither hat es stets an Beliebtheit in der Entwickler-Community gewonnen und ist mittlerweile ein fester Bestandteil vieler Anwendungen.

RxSwift besteht aus fünf Modulen. Sie sind inklusive ihrer Abhängigkeiten in der folgenden Abbildung dargestellt:

RxSwift und RxTest werden in den folgenden Beispielen praktisch verwendet (Abb. 2).

(Bild: https://github.com/ReactiveX/RxSwift)

  • RxSwift: Kernmodul, stellt die Standards bereit, die ReactiveX definiert.
  • RxCocoa: enthält einige Erweiterungen, die die Entwicklung von Apps mit RxSwift erleichtern.
  • RxRelay: Wrapper um Subjects.
  • RxTest und RxBlocking: ermöglichen die Testbarkeit von RxSwift-Code.

Laut der Dokumentation von RxSwift versuchen die Entwickler die Vorgaben von ReactiveX so genau wie möglich abzubilden. Es gibt jedoch einige Unterschiede und Erweiterungen. Ein elementarer Unterschied sind Traits. Das sind praktische Zusätze in RxSwift, obwohl es dazu keine Spezifikation in ReactiveX gibt. Der Grund dafür ist das starke Typsystem von Swift. Durch dieses lassen sich die Korrektheit und die Stabilität von Anwendungen erhöhen, die RxSwift nutzen. Das erreichen Traits, indem sie die Eigenschaften, die ein Observable haben kann, über Schnittstellen hinweg sicherstellen. Außerdem werden die Observables durch den Einsatz von Traits deskriptiv. Es existieren die drei folgenden Traits:

  • Single: erzeugt keine Folge von Events, sondern genau ein Event oder einen Fehler. Ein sinnvolles Beispiel sind HTTP-Requests.
  • Completeable: erzeugt keine Folge von Events, sondern ein onComplete- oder onError-Event. Ein sinnvolles Beispiel sind langläufige Tasks, bei denen nur der erfolgreiche Abschluss, nicht aber das Ergebnis interessiert.
  • Maybe: kombiniert Single sowie Completeable und gibt entweder genau ein Event, ein onError oder ein onComplete aus.

Traits sind optional, und der Aufruf von .asObservable() auf einem Trait löst die Konvertierung in ein normales Observable aus.

Nach den theoretischen Grundlagen von ReactiveX und den Hintergründen zu RxSwift geht es nun an erste Code-Beispiele. Um diese ausführen zu können, ist ein neuer Xcode-Workspace anzulegen und der Pod RxSwift im Podfile hinzuzufügen. Alle Beispiele werden als Tests implementiert. Die Code-Snippets des Artikels sind auf GitHub zu finden.

Das HelloWorld-Beispiel für RxSwift zeigt das Erzeugen eines Observables sowie einer Subscription darauf, die die Events in der Konsole ausgibt:

func testHelloRxSwift() {
let observable = Observable.from([1, 2, 3])
observable.subscribe { event in print(event)}
}

Die Ausführung des Tests gibt unter anderem folgende Werte in der Konsole aus:

next(1)
next(2)
next(3)
completed

Die ersten drei Zeilen geben die erwarteten Events wieder. Das Array, das das Observable erzeugt hat, enthält die Integers 1, 2 und 3. In derselben Reihenfolge ruft das Observable die next()-Methode auf dem Observer auf und übergibt jeweils das entsprechende Integer des Arrays als Argument.

Die vierte Zeile gibt ein weiteres Event aus – completed. Der From-Operator, der das Observable erzeugt, erhält ein endliches Array als Eingabeparameter. Nachdem das Observable alle Elemente des Arrays emittiert hat, kann es keine weiteren Events geben. Deswegen wird das completed-Event an die Observer übermittelt. Daraus lässt sich schließen, dass die Quelle der Events versiegt ist und das Observable keine weiteren Events mehr erzeugen wird.

Eine etwas erweiterte Version des HelloWorld-Beispiels sieht wie folgt aus:

let disposeBag = DisposeBag()
unc testHelloRxSwiftExtended() {
let observable = Observable.from([1, 2, 3])
observable.subscribe { event in
switch event {
case .next(let value):
print(value)
case .error(let error):
print(error)
case .completed:
print("Completed")
}
}
}.disposed(by: disposeBag)

Die Ausführung dieses Tests erzeugt unter anderem die folgende Ausgabe in der Konsole:

1
2
3
Completed

Neu ist im Vergleich zum vorherigen Beispiel, dass der Observer ein switch-Statement implementiert. Das verdeutlicht noch einmal, dass ein Observable drei unterschiedliche Events emittieren kann:

  • Next
  • Error
  • Completed

Das switch-Statement implementiert für jedes dieser Events eine Funktion, die entweder den übergebenen Parameter (1, 2 oder 3) oder den übermittelten Fehler ausgibt. Beim Completed-Event wird ein entsprechender String ausgegeben.

Ein weiterer Unterschied ist, dass auf dem Observer .disposed(by: disposeBag) aufgerufen wird. Das verhindert die Entstehung eines Speicherlecks.