Swift Language
RxSwift
Ricerca…
Nozioni di base su RxSwift
FRP, o Functional Reactive Programming, ha alcuni termini di base che è necessario conoscere.
Ogni pezzo di dati può essere rappresentato come Observable
, che è un flusso di dati asincrono. La potenza di FRP è rappresentata da eventi sincroni e asincroni come flussi, Observable
e fornendo la stessa interfaccia per lavorare con esso.
Solitamente Observable
contiene diversi (o nessuno) eventi che .Next
la data - .Next
Eventi successivi, e quindi può essere terminata con successo ( .Success
) o con un errore ( .Error
).
Diamo un'occhiata al seguente diagramma di marmo:
--(1)--(2)--(3)|-->
In questo esempio c'è un flusso di valori Int
. Con il passare del tempo, tre eventi. .Next
sono verificati e quindi il flusso è stato interrotto correttamente.
--X->
Il diagramma sopra mostra un caso in cui non sono stati emessi dati e. .Error
evento di .Error
termina l' Observable
.
Prima di andare avanti, ci sono alcune risorse utili:
- RxSwift . Guarda gli esempi, leggi i documenti e inizia.
- RxSwift Slack room ha alcuni canali per la risoluzione dei problemi educativi.
- Gioca con RxMarbles per sapere cosa fa l'operatore e quale è il più utile nel tuo caso.
- Dai un'occhiata a questo esempio , esplora il codice da solo.
Creare osservabili
RxSwift offre molti modi per creare un Observable
, diamo un'occhiata:
import RxSwift
let intObservale = Observable.just(123) // Observable<Int>
let stringObservale = Observable.just("RxSwift") // Observable<String>
let doubleObservale = Observable.just(3.14) // Observable<Double>
Quindi, gli osservabili sono creati. Essi contengono solo un valore e quindi termina con successo. Tuttavia, non è successo nulla dopo che è stato creato. Perché?
Ci sono due passaggi per lavorare con Observable
s: osservi qualcosa per creare uno stream e poi ti iscrivi allo stream o lo leghi a qualcosa per interagire con esso.
Observable.just(12).subscribe {
print($0)
}
La console stamperà:
.Next(12)
.Completed()
E se mi interessa solo lavorare con i dati, che si svolgono in .Next
Eventi successivi, vorrei utilizzare l'operatore subscribeNext
:
Observable.just(12).subscribeNext {
print($0) // prints "12" now
}
Se voglio creare un osservabile di molti valori, utilizzo diversi operatori:
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.
E infine, forse voglio un Observable
che funzioni. Ad esempio, è conveniente racchiudere un'operazione di rete in Observable<SomeResultType>
. Diamo un'occhiata a fare uno può ottenere questo:
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.
}
smaltimento
Dopo aver creato la sottoscrizione, è importante gestirne la corretta deallocazione.
I documenti ce l'hanno detto
Se una sequenza termina in un tempo limitato, non chiamare dispose o non usare addDisposableTo (disposeBag) non causerà alcuna perdita permanente di risorse. Tuttavia, tali risorse verranno utilizzate fino al completamento della sequenza, terminando la produzione di elementi o restituendo un errore.
Esistono due modi per deallocare le risorse.
- Utilizzo di
disposeBag
s eaddDisposableTo
operator. - Usando
takeUntil
operatoretakeUntil
.
Nel primo caso si passa manualmente l'abbonamento all'oggetto DisposeBag
, che cancella correttamente tutta la memoria acquisita.
let bag = DisposeBag()
Observable.just(1).subscribeNext {
print($0)
}.addDisposableTo(bag)
In realtà non è necessario creare DisposeBag
in ogni classe che si crea, basta dare un'occhiata al progetto RxSwift Community denominato NSObject + Rx . Utilizzando il framework il codice sopra può essere riscritto come segue:
Observable.just(1).subscribeNext {
print($0)
}.addDisposableTo(rx_disposeBag)
Nel secondo caso, se il tempo di sottoscrizione coincide con la durata dell'oggetto self
, è possibile implementare lo smaltimento utilizzando takeUntil(rx_deallocated)
:
let _ = sequence
.takeUntil(rx_deallocated)
.subscribe {
print($0)
}
Attacchi
Observable.combineLatest(firstName.rx_text, lastName.rx_text) { $0 + " " + $1 }
.map { "Greetings, \($0)" }
.bindTo(greetingLabel.rx_text)
Usando l'operatore combineLatest
ogni volta che un oggetto viene emesso da uno dei due Observables
, combinare l'ultimo oggetto emesso da ciascun Observable
. In questo modo combiniamo il risultato dei due UITextField
creando un nuovo messaggio con il testo "Greetings, \($0)"
usando l'interpolazione della stringa per legare in seguito al testo di un UILabel
.
Possiamo legare i dati a qualsiasi UITableView
e UICollectionView
in un modo molto semplice:
viewModel
.rows
.bindTo(resultsTableView.rx_itemsWithCellIdentifier("WikipediaSearchCell", cellType: WikipediaSearchCell.self)) { (_, viewModel, cell) in
cell.title = viewModel.title
cell.url = viewModel.url
}
.addDisposableTo(disposeBag)
Questo è un wrapper Rx attorno al metodo di origine dati cellForRowAtIndexPath
. E anche Rx si prende cura dell'implementazione di numberOfRowsAtIndexPath
, che è un metodo obbligatorio in senso tradizionale, ma non è necessario implementarlo qui, è curato.
RxCocoa e ControlEvents
RxSwift fornisce non solo i modi per controllare i dati, ma anche per rappresentare le azioni dell'utente in modo reattivo.
RxCocoa contiene tutto ciò di cui hai bisogno. Trasporta la maggior parte delle proprietà dei componenti dell'interfaccia utente in s Observable
, ma non proprio. Ci sono alcuni Observable
aggiornati chiamati ControlEvent
(che rappresentano eventi) e ControlProperties
(che rappresentano proprietà, sorpresa!). Queste cose Observable
flussi Observable
sotto il cofano, ma hanno anche alcune sfumature:
- Non fallisce mai, quindi nessun errore.
-
Complete
sequenza sul controllo che viene deallocato. - Fornisce eventi sul thread principale (
MainScheduler.instance
).
Fondamentalmente, puoi lavorare con loro come al solito:
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.
Questo è molto importante da usare: finché usi Rx, dimenticati del materiale @IBAction
, tutto ciò di cui hai bisogno puoi collegarlo e configurarlo contemporaneamente. Ad esempio, il metodo viewDidLoad
del tuo controller di visualizzazione è un buon candidato per descrivere come funzionano i componenti dell'interfaccia utente.
Ok, un altro esempio: supponiamo di avere un campo di testo, un pulsante e un'etichetta. Vogliamo validare il testo nel campo di testo quando si tocca il pulsante, e visualizzare i risultati in etichetta. Sì, sembra un altro compito di email di conferma, eh?
Prima di tutto, prendiamo il button.rx_tap
ControlEvent:
----()-----------------------()----->
Qui le parentesi vuote mostrano i tocchi dell'utente. Successivamente, prendiamo ciò che è scritto nel campo testo con withLatestFrom
operatore withLatestFrom
( withLatestFrom
un'occhiata qui , immaginate che il flusso superiore rappresenti i tocchi utente, quello in basso rappresenta il testo nel campo di testo).
button.rx_tap.withLatestFrom(textField.rx_text)
----("")--------------------("123")--->
// ^ tap ^ i wrote 123 ^ tap
Bello, abbiamo un flusso di stringhe da convalidare, emesse solo quando abbiamo bisogno di convalidare.
Qualsiasi Observable
ha operatori familiari come la map
o il filter
, prenderemo la map
per convalidare il testo. Crea tu stesso la funzione di validateEmail
mail, usa qualsiasi regex che desideri.
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)
Fatto! Se hai bisogno di più logica personalizzata (come mostrare le visualizzazioni di errore in caso di errore, effettuare una transizione su un altro schermo in caso di successo ...), iscriviti al flusso Bool
finale e scrivilo lì.