Llenar la brecha de KVO en Swift con ReactiveCocoa

¿Cómo se hace la observación de valor clave en Swift? ¿Puedes? ¿Y qué hay de usar el mecanismo KVO nativo de Objective-C? Lo que sigue es una introducción suave al héroe no reconocido de ReactiveCocoa en la observación de propiedades: Property (y MutableProperty).

Matt Thompson explicó los entresijos de KVO en detalles exasperantes en 2013 para NSHipster. En resumen, digamos que es una API Objective-C desordenada, que todavía funciona en Swift hoy. Aparte de las ocasiones en que no puede prescindir (¿NSProgress a alguien?), Le aconsejaría que no lo use. Su complejidad lo hace propenso a los errores. Pero Swift no ofrece un equivalente nativo, dices …

Te escucho, pero supongamos que simplemente puedes hacer algo como esto, para escuchar los cambios y volver a cargar una vista de tabla en respuesta, recogiendo cualquier elemento nuevo:

  anular func viewDidLoad () { 
super.viewDidLoad ()
  viewModel.drafts.signal.observeValues ​​{[self débil] borradores en 
self? .tableView.reloadData ()
}
}

Conozca las propiedades de ReactiveSwift

Omitiría un dato importante sobre las capacidades de observación nativas de Swift, si no mencionara los observadores de propiedad didSet y willSet . Con suficiente código repetitivo, definitivamente puedes improvisar algo similar a KVO. Podría, por ejemplo, escuchar los cambios de propiedad usted mismo con didSet y reenviar estos eventos a través de un delegado. Los puristas preferirán este enfoque, porque no hay magia involucrada y no se requieren bibliotecas de terceros.

Sin embargo, ReactiveSwift tiene como objetivo encapsular toda esta complejidad por medio de un concepto singular, llamado Propiedad . Simple, porque en esencia, la Propiedad es simplemente un cuadro de valor, con un medio para notificar a otros de sus cambios. Aquí está el protocolo reducido, tomado de la versión 1.0 de ReactiveSwift:

  protocolo público PropertyProtocol: clase, BindingSourceProtocol { 
Valor de tipo asociado

valor var: valor {get}
productor var: SignalProducer {get}
señal var: Señal {get}
}

Las propiedades de productor y señal son los observables reales a los que se suscribe para los cambios. ¿Por qué la distinción? El RxJS README tiene un buen resumen:

“Los observables fríos comienzan a ejecutarse con la suscripción, es decir, la secuencia observable solo comienza a enviar valores a los observadores cuando se llama a Suscribirse. Los valores tampoco se comparten entre los suscriptores. Esto es diferente de los observables en caliente, como los eventos de movimiento del mouse o las fichas de acciones que ya están produciendo valores incluso antes de que una suscripción esté activa “.

Cuando se aplica a ReactiveSwift, los observables en frío corresponden a los productores de señales, mientras que las señales calientes están representadas por señales “regulares”. En el contexto de las propiedades, esto se reduce a:

  • Al iniciar el productor se emitirá el valor inicial de la propiedad, así como cualquier cambio posterior;
  • Observar la señal solo emitirá los valores cambiados después de que se inició la observación.

Uso

Las implementaciones concretas de PropertyProtocol están contenidas en las clases Property y MutableProperty. En general, usará la variante mutable, ya que le permite cambiar el valor subyacente de la propiedad, lo que desencadena notificaciones de cambio. La forma más fácil es renunciar a las opciones y usar constantes let en su lugar:

  // Definiendo la propiedad 
let avatarUrl = MutableProperty ("https://httpbin.org/image/jpeg")
  // Establecer el valor de la propiedad 
avatarUrl.value = "..."
  // Escuchando cambios, sin el valor inicial avatarUrl.signal.observeValues ​​{url in 
...
}
  // Escuchando los cambios, incluido el valor inicial avatarUrl.producer.startWithValues ​​{url in 
...
}

El enlace de datos

El enlace de datos es un medio para vincular propiedades a los componentes de la interfaz de usuario, de modo que el componente se actualice solo cuando cambie la propiedad. Contrariamente a AppKit, el UIKit de iOS no tiene noción de enlace de datos. ReactiveCocoa expone el enlace de datos a través de una única propiedad personalizada: un signo menor que seguido de una tilde.

  <~ 

Su forma enfatiza la unidireccionalidad del enlace. El siguiente ejemplo expresa claramente el hecho de que el nombre del artista se establece en la propiedad de texto de la etiqueta del artista cada vez que cambia, y no al revés:

  // Conecta enlaces en la vista de tu controladorDidLoad () 
func viewDidLoad () {
super.viewDidLoad ()
artistLabel.reactive.text <~ viewModel.artistName
}
  // Ejemplo de implementación de este modelo de vista (detalles omitidos) 
clase ViewModel {
let artistName = MutableProperty (nil)
}

El enlace de datos es una forma declarativa de expresar relaciones entre datos y componentes de la interfaz de usuario. Reduce la sobrecarga cognitiva de asignar valores manualmente. La localidad del código también se mejora, ya que este enfoque centra la lógica vinculante en un solo lugar.

ReactiveCocoa y ReactiveSwift

¿Cual es la diferencia? Con ReactiveCocoa 5, el equipo central decidió dividir la base de código, para delinear más claramente la responsabilidad y el alcance de cada componente. Esta división también permite a los usuarios del marco seleccionar más fácilmente qué módulos necesitan en sus proyectos.

  • ReactiveSwift : El nuevo chico en el bloque. Esta es una implementación puramente rápida de la API reactiva. Proporciona todas las tuercas y tornillos, incluidas las señales, los productores de señales, las propiedades, las acciones y los enlaces.
  • ReactiveObjC : hermano basado en Objective-C de ReactiveSwift. Esto es esencialmente ReactiveCocoa 2.0 y permanece en modo de mantenimiento.
  • ReactiveCocoa : la pieza de conexión entre UIKit y ReactiveSwift. Proporciona todas las extensiones reactivas para los componentes UIKit y expone los objetivos vinculantes para ellos. También presenta Triggers, un medio para convertir los selectores de Objective-C en señales. También puede envolver un buen ObjC KVO con señales, para que se puedan intercalar con señales y productores ReactiveSwift.
  • ReactiveObjCBridge : une la API ReactiveCocoa 2.0 con la API ReactiveSwift, por lo que ReactiveObjC permanece accesible desde Swift, lo que permite la interoperabilidad con el código antiguo.