Swift Language
RxSwift
Suche…
RxSwift-Grundlagen
FRP oder Functional Reactive Programming hat einige grundlegende Begriffe, die Sie kennen müssen.
Jedes Datenelement kann als Observable
, bei dem es sich um einen asynchronen Datenstrom handelt. Die Leistungsfähigkeit von FRP besteht darin, synchrone und asynchrone Ereignisse als Streams, Observable
s, darzustellen und dieselbe Schnittstelle für die Arbeit damit zu bieten.
Normalerweise enthält Observable
mehrere (oder keine) Ereignisse, die das Datum enthalten - .Next
Ereignisse. Anschließend können sie erfolgreich ( .Success
) oder mit einem Fehler ( .Error
) beendet werden.
Schauen wir uns das folgende Marmordiagramm an:
--(1)--(2)--(3)|-->
In diesem Beispiel gibt es einen Strom von Int
Werten. Mit der Zeit traten drei .Next
Ereignisse auf, und der Stream wurde erfolgreich beendet.
--X->
Das obige Diagramm zeigt einen Fall, in dem keine Daten .Error
und das .Error
Ereignis das Observable
beendet.
Bevor wir fortfahren, gibt es einige nützliche Ressourcen:
- RxSwift . Sehen Sie sich Beispiele an, lesen Sie Dokumente und machen Sie sich an die Arbeit.
- Im RxSwift Slack-Raum gibt es einige Kanäle zur Lösung von Problemen mit dem Bildungswesen.
- Spielen Sie mit RxMarbles herum, um zu erfahren, was der Operator tut und welcher in Ihrem Fall am nützlichsten ist.
- Schauen Sie sich dieses Beispiel an , erkunden Sie den Code selbst.
Observable erstellen
RxSwift bietet viele Möglichkeiten, ein Observable
zu erstellen, werfen wir einen Blick darauf:
import RxSwift
let intObservale = Observable.just(123) // Observable<Int>
let stringObservale = Observable.just("RxSwift") // Observable<String>
let doubleObservale = Observable.just(3.14) // Observable<Double>
So werden die Observablen erstellt. Sie haben nur einen Wert und enden dann mit Erfolg. Trotzdem passiert nichts, nachdem es erstellt wurde. Warum?
Es gibt zwei Schritte bei der Arbeit mit Observable
s: Sie beobachten etwas, um einen Stream zu erstellen, und dann abonnieren Sie den Stream oder binden ihn an etwas, um damit zu interagieren .
Observable.just(12).subscribe {
print($0)
}
Die Konsole druckt:
.Next(12)
.Completed()
Und wenn ich nur mit Daten arbeiten möchte, die in .Next
Ereignissen stattfinden, würde ich den subscribeNext
Operator verwenden:
Observable.just(12).subscribeNext {
print($0) // prints "12" now
}
Wenn ich ein Observable mit vielen Werten erstellen möchte, verwende ich verschiedene Operatoren:
Observable.of(1,2,3,4,5).subscribeNext {
print($0)
}
// 1
// 2
// 3
// 4
// 5
// I can represent existing data types as Observables also:
[1,2,3,4,5].asObservable().subscribeNext {
print($0)
}
// result is the same as before.
Und schließlich möchte ich vielleicht ein Observable
, das etwas Arbeit leistet. Zum Beispiel ist es praktisch, einen Netzwerkvorgang in Observable<SomeResultType>
. Werfen wir einen Blick darauf, wie man das erreichen kann:
Observable.create { observer in // create an Observable ...
MyNetworkService.doSomeWorkWithCompletion { (result, error) in
if let e = error {
observer.onError(e) // ..that either holds an error
} else {
observer.onNext(result) // ..or emits the data
observer.onCompleted() // ..and terminates successfully.
}
}
return NopDisposable.instance // here you can manually free any resources
//in case if this observable is being disposed.
}
Entsorgen
Nachdem das Abonnement erstellt wurde, ist es wichtig, seine korrekte Freigabe zu verwalten.
Die Dokumente haben uns das gesagt
Wenn eine Sequenz in endlicher Zeit beendet wird, verursacht das Aufrufen von dispose oder das Verwenden von addDisposableTo (disposeBag) keine permanenten Ressourcenlecks. Diese Ressourcen werden jedoch bis zum Abschluss der Sequenz verwendet, entweder durch Abschluss der Produktion von Elementen oder durch Rückgabe eines Fehlers.
Es gibt zwei Möglichkeiten, Ressourcen freizugeben.
- Verwenden
disposeBag
addDisposableTo
Operators s undaddDisposableTo
. -
takeUntil
Operator verwenden.
Im ersten Fall übergeben Sie das Abonnement manuell an das DisposeBag
Objekt, das den DisposeBag
Speicherplatz ordnungsgemäß löscht.
let bag = DisposeBag()
Observable.just(1).subscribeNext {
print($0)
}.addDisposableTo(bag)
Sie müssen nicht in jeder von Ihnen erstellten Klasse DisposeBag
s erstellen. DisposeBag
Sie sich einfach das RxSwift Community -Projekt mit dem Namen NSObject + Rx an . Mit Hilfe des Frameworks kann der Code wie folgt umgeschrieben werden:
Observable.just(1).subscribeNext {
print($0)
}.addDisposableTo(rx_disposeBag)
Im zweiten Fall, wenn die Abonnementzeit mit der Lebensdauer des self
übereinstimmt, ist es möglich, die Entsorgung mit takeUntil(rx_deallocated)
zu implementieren:
let _ = sequence
.takeUntil(rx_deallocated)
.subscribe {
print($0)
}
Bindungen
Observable.combineLatest(firstName.rx_text, lastName.rx_text) { $0 + " " + $1 }
.map { "Greetings, \($0)" }
.bindTo(greetingLabel.rx_text)
Verwenden Sie den combineLatest
Operator jedes Mal, wenn ein Objekt von einem der beiden Observables
combineLatest
wird, das letzte Element, das von jedem Observable
. Auf diese Weise kombinieren wir auf diese Weise das Ergebnis der Erstellung einer neuen Nachricht durch den UITextField
mit dem Text "Greetings, \($0)"
Verwendung der String-Interpolation, um später an den Text eines UILabel
zu binden.
Wir können Daten auf einfache Weise an jede UITableView
und UICollectionView
binden:
viewModel
.rows
.bindTo(resultsTableView.rx_itemsWithCellIdentifier("WikipediaSearchCell", cellType: WikipediaSearchCell.self)) { (_, viewModel, cell) in
cell.title = viewModel.title
cell.url = viewModel.url
}
.addDisposableTo(disposeBag)
cellForRowAtIndexPath
ist ein Rx-Wrapper für die Datenquellenmethode cellForRowAtIndexPath
. Und Rx kümmert sich auch um die Implementierung von numberOfRowsAtIndexPath
, einer Methode, die im traditionellen Sinne erforderlich ist, aber Sie müssen sie hier nicht implementieren, sie wird numberOfRowsAtIndexPath
.
RxCocoa und ControlEvents
RxSwift bietet nicht nur die Möglichkeit, Ihre Daten zu kontrollieren, sondern auch Benutzeraktionen auf reaktive Weise darzustellen.
RxCocoa enthält alles was Sie brauchen. Es packt die meisten Eigenschaften der UI-Komponenten in Observable
s ein, aber nicht wirklich. Es gibt einige aktualisierte Observable
ControlEvent
namens ControlEvent
s (die Ereignisse darstellen) und ControlProperties
(die Eigenschaften darstellen, Überraschung!). Diese Dinge halten Observable
Ströme unter der Haube, sondern haben auch einige Nuancen:
- Es versagt nie, also keine Fehler.
- Es wird
Complete
Sie Sequenz auf Steuerausgeplant werden. - Es liefert Ereignisse im Hauptthread (
MainScheduler.instance
).
Grundsätzlich können Sie wie gewohnt mit ihnen arbeiten:
button.rx_tap.subscribeNext { _ in // control event
print("User tapped the button!")
}.addDisposableTo(bag)
textField.rx_text.subscribeNext { text in // control property
print("The textfield contains: \(text)")
}.addDisposableTo(bag)
// notice that ControlProperty generates .Next event on subscription
// In this case, the log will display
// "The textfield contains: "
// at the very start of the app.
Dies ist sehr wichtig für die Verwendung: @IBAction
Sie Rx verwenden, vergessen Sie den @IBAction
Zeugs. Alles, was Sie brauchen, können Sie sofort binden und konfigurieren. Die viewDidLoad
Methode Ihres View-Controllers ist beispielsweise ein guter Kandidat, um die Funktionsweise der UI-Komponenten zu beschreiben.
Ok, ein anderes Beispiel: Nehmen wir an, wir haben ein Textfeld, eine Schaltfläche und eine Bezeichnung. Wir möchten Text im Textfeld überprüfen, wenn wir auf die Schaltfläche tippen , und die Ergebnisse in der Beschriftung anzeigen . Ja, es scheint eine weitere Validate-E-Mail-Aufgabe zu sein, oder?
Zuerst greifen wir das button.rx_tap
ControlEvent:
----()-----------------------()----->
Hier zeigen leere Klammern die Benutzeranschläge an. Als Nächstes nehmen wir das, was in textField geschrieben ist, mit dem withLatestFrom
Operator (sehen Sie sich das hier an , stellen Sie sich vor , dass der obere Stream Benutzer-Taps darstellt, der untere Text den Text im Textfeld).
button.rx_tap.withLatestFrom(textField.rx_text)
----("")--------------------("123")--->
// ^ tap ^ i wrote 123 ^ tap
Schön, wir haben einen Strom von Strings zu validieren, der nur dann ausgegeben wird, wenn wir validieren müssen.
Jedes Observable
verfügt über vertraute Operatoren wie map
oder filter
. Wir nehmen map
zur Validierung des Texts. Erstellen Sie die validateEmail
Funktion selbst und verwenden Sie einen beliebigen regulären Ausdruck.
button.rx_tap // ControlEvent<Void>
.withLatestFrom(textField.rx_text) // Observable<String>
.map(validateEmail) // Observable<Bool>
.map { (isCorrect) in
return isCorrect ? "Email is correct" : "Input the correct one, please"
} // Observable<String>
.bindTo(label.rx_text)
.addDisposableTo(bag)
Erledigt! Wenn Sie mehr benutzerdefinierte Logik benötigen (z. B. Fehleransichten anzeigen, bei Erfolg zu einem anderen Bildschirm wechseln ...), abonnieren Sie einfach den endgültigen Bool
Stream und schreiben Sie ihn dort.