Swift Language
RxSwift
Zoeken…
RxSwift basisprincipes
FRP, of Functioneel Reactief Programmeren, heeft enkele basisbegrippen die u moet kennen.
Elk stuk gegevens kan worden weergegeven als Observable
, wat een asynchrone gegevensstroom is. De kracht van FRP zit in de weergave van synchrone en asynchrone gebeurtenissen als streams, Observable
s, en biedt dezelfde interface om ermee te werken.
Meestal Observable
heeft verschillende (of geen) gebeurtenissen die de datum bevat - .Next
gebeurtenissen, en dan kan worden beëindigd met succes ( .Success
) of met een fout ( .Error
).
Laten we het volgende marmeren diagram eens bekijken:
--(1)--(2)--(3)|-->
In dit voorbeeld is er een stroom van Int
waarden. Naarmate de tijd .Next
, vonden drie .Next
gebeurtenissen plaats en vervolgens werd de stream met succes beëindigd.
--X->
Het bovenstaande diagram toont een geval waarin geen gegevens zijn uitgezonden en .Error
gebeurtenis beëindigt de Observable
.
Voordat we verder gaan, zijn er enkele nuttige bronnen:
- RxSwift . Bekijk voorbeelden, lees documenten en ga aan de slag.
- RxSwift Slack room heeft een paar kanalen voor het oplossen van onderwijsproblemen.
- Speel met RxMarbles om te weten wat de operator doet en welke in uw geval het handigst is.
- Bekijk dit voorbeeld en verken de code zelf.
Waarnemingen maken
RxSwift biedt vele manieren om een te creëren Observable
, laten we eens een kijkje nemen:
import RxSwift
let intObservale = Observable.just(123) // Observable<Int>
let stringObservale = Observable.just("RxSwift") // Observable<String>
let doubleObservale = Observable.just(3.14) // Observable<Double>
Dus, de observables zijn gemaakt. Ze hebben slechts één waarde en worden vervolgens met succes beëindigd. Toch gebeurde er niets nadat het was gemaakt. Waarom?
Er zijn twee stappen in het werken met Observable
s: je observeert iets om een stream te maken en je abonneert je vervolgens op de stream of verbindt het aan iets om ermee te communiceren .
Observable.just(12).subscribe {
print($0)
}
De console zal afdrukken:
.Next(12)
.Completed()
En als ik alleen geïnteresseerd zou zijn in het werken met gegevens, die plaatsvinden in .Next
evenementen, zou ik de operator subscribeNext
gebruiken:
Observable.just(12).subscribeNext {
print($0) // prints "12" now
}
Als ik een waarneembare waarde van meerdere waarden wil creëren, gebruik ik verschillende 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.
En tot slot wil ik misschien een Observable
die wat werk doet. Het is bijvoorbeeld handig om een netwerkbewerking in Observable<SomeResultType>
. Laten we eens kijken of iemand dit kan bereiken:
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.
}
afvoeren
Nadat het abonnement is gemaakt, is het belangrijk om de juiste deallocatie te beheren.
Dat hebben de documenten ons verteld
Als een reeks eindigt in een eindige tijd, zal het niet gebruiken van dispose of het niet gebruiken van addDisposableTo (disposeBag) geen permanente lekken veroorzaken. Deze bronnen worden echter gebruikt totdat de reeks is voltooid, hetzij door de productie van elementen te voltooien of een fout te retourneren.
Er zijn twee manieren om middelen toe te wijzen.
-
disposeBag
s enaddDisposableTo
operator gebruiken. -
takeUntil
operator gebruiken.
In het eerste geval geeft u het abonnement handmatig door aan het object DisposeBag
, waarmee alle opgenomen geheugen correct wordt gewist.
let bag = DisposeBag()
Observable.just(1).subscribeNext {
print($0)
}.addDisposableTo(bag)
Het is niet nodig om DisposeBag
s te maken in elke klasse die u maakt, kijk maar eens naar het RxSwift Community- project NSObject + Rx . Met behulp van het framework kan de bovenstaande code als volgt worden herschreven:
Observable.just(1).subscribeNext {
print($0)
}.addDisposableTo(rx_disposeBag)
In het tweede geval de abonnementstijd samenvalt met de self
object levensduur, is het mogelijk uit te voeren aanbrengen behulp takeUntil(rx_deallocated)
:
let _ = sequence
.takeUntil(rx_deallocated)
.subscribe {
print($0)
}
Bindingen
Observable.combineLatest(firstName.rx_text, lastName.rx_text) { $0 + " " + $1 }
.map { "Greetings, \($0)" }
.bindTo(greetingLabel.rx_text)
Gebruik de operator combineLatest
telkens wanneer een item wordt uitgezonden door een van de twee Observables
, combineer het laatste item dat wordt uitgezonden door elke Observable
. Dus op deze manier combineren we het resultaat van het creëren van een nieuw bericht door de twee UITextField
met de tekst "Greetings, \($0)"
met stringinterpolatie om later te binden aan de tekst van een UILabel
.
We kunnen gegevens binden aan elke UITableView
en UICollectionView
op een zeer eenvoudige manier:
viewModel
.rows
.bindTo(resultsTableView.rx_itemsWithCellIdentifier("WikipediaSearchCell", cellType: WikipediaSearchCell.self)) { (_, viewModel, cell) in
cell.title = viewModel.title
cell.url = viewModel.url
}
.addDisposableTo(disposeBag)
Dat is een Rx-wrapper rond de gegevensbronmethode cellForRowAtIndexPath
. En ook Rx zorgt voor de implementatie van het numberOfRowsAtIndexPath
, een vereiste methode in traditionele zin, maar u hoeft het hier niet te implementeren, het is geregeld.
RxCocoa en ControlEvents
RxSwift biedt niet alleen de manieren om uw gegevens te beheren, maar ook om acties van gebruikers op een reactieve manier weer te geven.
RxCocoa bevat alles wat u nodig heeft. Het verpakt de meeste eigenschappen van de UI-componenten in Observable
s, maar niet echt. Er zijn enkele opgewaardeerde Observable
s genaamd ControlEvent
s (die gebeurtenissen vertegenwoordigen) en ControlProperties
(die eigenschappen vertegenwoordigen, verrassing!). Deze dingen bevatten Observable
streams onder de motorkap, maar hebben ook enkele nuances:
- Het faalt nooit, dus geen fouten.
- De reeks wordt
Complete
controle wordt toegewezen. - Het levert gebeurtenissen op de
MainScheduler.instance
(MainScheduler.instance
).
Kortom, u kunt zoals gewoonlijk met hen werken:
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.
Dit is erg belangrijk om te gebruiken: zolang je Rx gebruikt, vergeet dan het @IBAction
spul, alles wat je nodig hebt kun je in één keer binden en configureren. De methode viewDidLoad
van uw view-controller is bijvoorbeeld een goede kandidaat om te beschrijven hoe de UI-componenten werken.
Ok, nog een voorbeeld: stel dat we een tekstveld, een knop en een label hebben. We willen de tekst in het tekstveld te valideren wanneer we op de knop, en de resultaten in de label weer te geven. Yep, lijkt me een zoveelste e-mailtaak, toch?
Allereerst pakken we de button.rx_tap
ControlEvent:
----()-----------------------()----->
Hier tonen lege haakjes gebruikerstappen. Vervolgens nemen we wat er in de withLatestFrom
is geschreven met de operator withLatestFrom
(bekijk het hier , stel je voor dat de bovenste stream staat voor gebruikerstappen, de onderste voor tekst in het tekstveld).
button.rx_tap.withLatestFrom(textField.rx_text)
----("")--------------------("123")--->
// ^ tap ^ i wrote 123 ^ tap
Leuk, we hebben een reeks strings om te valideren, alleen uitgezonden wanneer we moeten valideren.
Elke Observable
heeft vertrouwde operatoren zoals map
of filter
, we nemen map
om de tekst te valideren. Maak zelf de functie validateEmail
, gebruik elke gewenste regex.
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)
Gedaan! Als je meer aangepaste logica nodig hebt (zoals het weergeven van foutweergaven in het geval van fouten, het maken van een overgang naar een ander scherm bij succes ...), abonneer je gewoon op de laatste Bool
stream en schrijf het daar.