Swift Language
RxSwift
Buscar..
Conceptos básicos de RxSwift
FRP, o Programación reactiva funcional, tiene algunos términos básicos que debe conocer.
Cada dato puede representarse como Observable
, que es un flujo de datos asíncrono. El poder de FRP está en representación de eventos síncronos y asíncronos como flujos, Observable
s, y proporciona la misma interfaz para trabajar con él.
Por lo general, Observable
tiene varios eventos (o ninguno) que contienen la fecha: eventos .Next
, y luego puede terminarse con éxito ( .Success
) o con un error ( .Error
).
Echemos un vistazo al siguiente diagrama de mármol:
--(1)--(2)--(3)|-->
En este ejemplo hay un flujo de valores Int
. A medida que el tiempo avanza, se .Next
tres eventos .Next
y, a continuación, la secuencia terminó correctamente.
--X->
El diagrama anterior muestra un caso en el que no se emitieron datos y el evento .Error
finaliza el Observable
.
Antes de continuar, hay algunos recursos útiles:
- RxSwift . Mira ejemplos, lee documentos y comienza.
- RxSwift Slack room tiene algunos canales para resolver problemas de educación.
- Juega con RxMarbles para saber qué hace el operador y cuál es el más útil en tu caso.
- Echa un vistazo a este ejemplo , explora el código por ti mismo.
Creando observables
RxSwift ofrece muchas formas de crear un Observable
, echemos un vistazo:
import RxSwift
let intObservale = Observable.just(123) // Observable<Int>
let stringObservale = Observable.just("RxSwift") // Observable<String>
let doubleObservale = Observable.just(3.14) // Observable<Double>
Así, se crean los observables. Tienen un solo valor y luego terminan con éxito. Sin embargo, nada sucedió después de que fue creado. ¿Por qué?
Hay dos pasos para trabajar con Observable
s: observas algo para crear una secuencia y luego te suscribes a la secuencia o lo vinculas a algo para interactuar con ella.
Observable.just(12).subscribe {
print($0)
}
La consola imprimirá:
.Next(12)
.Completed()
Y si solo me interesa trabajar con datos, que tienen lugar en eventos .Next
, usaría el operador subscribeNext
:
Observable.just(12).subscribeNext {
print($0) // prints "12" now
}
Si quiero crear un observable de muchos valores, uso diferentes operadores:
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.
Y finalmente, tal vez quiero un Observable
que haga algo de trabajo. Por ejemplo, es conveniente envolver una operación de red en Observable<SomeResultType>
. Echemos un vistazo a uno puede lograr esto:
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.
}
Desechando
Una vez que se creó la suscripción, es importante administrar su desasignación correcta.
Los docs nos dijeron que
Si una secuencia termina en un tiempo finito, no llamar a dispose o no usar addDisposableTo (disposeBag) no causará ninguna fuga permanente de recursos. Sin embargo, esos recursos se utilizarán hasta que la secuencia se complete, ya sea terminando la producción de elementos o devolviendo un error.
Hay dos formas de desasignar recursos.
- Usando
disposeBag
sy el operadoraddDisposableTo
. - Utilizando el operador
takeUntil
.
En el primer caso, usted pasa manualmente la suscripción al objeto DisposeBag
, que borra correctamente toda la memoria tomada.
let bag = DisposeBag()
Observable.just(1).subscribeNext {
print($0)
}.addDisposableTo(bag)
En realidad, no es necesario crear DisposeBag
s en cada clase que cree, solo eche un vistazo al proyecto de la Comunidad RxSwift llamado NSObject + Rx . Usando el marco, el código anterior puede reescribirse como sigue:
Observable.just(1).subscribeNext {
print($0)
}.addDisposableTo(rx_disposeBag)
En el segundo caso, si el tiempo de suscripción coincide con el self
duración del objeto, es posible implementar la eliminación usando takeUntil(rx_deallocated)
:
let _ = sequence
.takeUntil(rx_deallocated)
.subscribe {
print($0)
}
Fijaciones
Observable.combineLatest(firstName.rx_text, lastName.rx_text) { $0 + " " + $1 }
.map { "Greetings, \($0)" }
.bindTo(greetingLabel.rx_text)
Usando el operador combineLatest
cada vez que un elemento sea emitido por cualquiera de los dos Observables
, combine el último elemento emitido por cada Observable
. Entonces, de esta manera, combinamos el resultado de los dos UITextField
's creando un nuevo mensaje con el texto "Greetings, \($0)"
utilizando la interpolación de cadenas para enlazar más tarde al texto de un UILabel
.
Podemos enlazar datos a cualquier UITableView
y UICollectionView
de una manera muy fácil:
viewModel
.rows
.bindTo(resultsTableView.rx_itemsWithCellIdentifier("WikipediaSearchCell", cellType: WikipediaSearchCell.self)) { (_, viewModel, cell) in
cell.title = viewModel.title
cell.url = viewModel.url
}
.addDisposableTo(disposeBag)
Eso es un envoltorio Rx alrededor del método de fuente de datos cellForRowAtIndexPath
. Y también Rx se encarga de la implementación de numberOfRowsAtIndexPath
, que es un método requerido en un sentido tradicional, pero no tiene que implementarlo aquí, se cuida.
RxCocoa y ControlEvents
RxSwift proporciona no solo las formas de controlar sus datos, sino que también representa las acciones de los usuarios de manera reactiva.
RxCocoa contiene todo lo que necesitas. Envuelve la mayoría de las propiedades de los componentes de la interfaz de usuario en Observable
s, pero no realmente. Hay algunos Observable
actualizados llamados ControlEvent
s (que representan eventos) y ControlProperties
(que representan propiedades, ¡sorpresa!). Estas cosas contienen corrientes Observable
bajo el capó, pero también tienen algunos matices:
- Nunca falla, así que no hay errores.
- Se
Complete
secuencia de control desasignada. - Ofrece eventos en el hilo principal (
MainScheduler.instance
).
Básicamente, puedes trabajar con ellos como de costumbre:
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.
Esto es muy importante de usar: siempre que uses Rx, olvídate de las cosas de @IBAction
, todo lo que necesites se puede vincular y configurar a la vez. Por ejemplo, el método viewDidLoad
de su controlador de vista es un buen candidato para describir cómo funcionan los componentes de la interfaz de usuario.
Ok, otro ejemplo: supongamos que tenemos un campo de texto, un botón y una etiqueta. Queremos validar el texto en el campo de texto cuando nos toque el botón y mostrar los resultados en la etiqueta. Sí, parece otra tarea de validar correo electrónico, ¿eh?
En primer lugar, tomamos el button.rx_tap
ControlEvent:
----()-----------------------()----->
Aquí los paréntesis vacíos muestran los grifos del usuario. A continuación, tomamos lo que está escrito en el campo de texto con el operador withLatestFrom
( withLatestFrom
un vistazo aquí , imagine que la secuencia superior representa los toques del usuario, la parte inferior representa el texto en el campo de texto).
button.rx_tap.withLatestFrom(textField.rx_text)
----("")--------------------("123")--->
// ^ tap ^ i wrote 123 ^ tap
Bien, tenemos una secuencia de cadenas para validar, emitidas solo cuando necesitamos validar.
Cualquier Observable
tiene operadores tan familiares como map
o filter
, tomaremos el map
para validar el texto. Cree la función validateEmail
usted mismo, use cualquier expresión regular que desee.
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)
¡Hecho! Si necesita más lógica personalizada (como mostrar vistas de error en caso de error, hacer una transición a otra pantalla en caso de éxito ...), simplemente suscríbase a la secuencia final de Bool
y escríbala allí.