Enlace de dos vías en RxSwift

Leí el operador binding de dos vías en el código de ejemplo de RxSwift.

func <-> <T>(property: ControlProperty<T>, variable: Variable<T>) -> Disposable { let bindToUIDisposable = variable.asObservable() .bindTo(property) let bindToVariable = property .subscribe(onNext: { n in variable.value = n }, onCompleted: { bindToUIDisposable.dispose() }) return StableCompositeDisposable.create(bindToUIDisposable, bindToVariable) } 

Cuando la property cambió, notificará la variable y establecerá el valor de la variable, mientras que el valor de la variable se establecerá, notificará la propiedad. Creo que conducirá a bucle infinito …

Gracias por suscitar la pregunta, pasé un time investigando la implementación de ControlProperty (tenga en count que agregué una llamada .debug() para rastrear los valores generados para la propiedad de control).

 public struct ControlProperty<PropertyType> : ControlPropertyType { public typealias E = PropertyType let _values: Observable<PropertyType> let _valueSink: AnyObserver<PropertyType> public init<V: ObservableType, S: ObserverType where E == VE, E == SE>(values: V, valueSink: S) { _values = values.debug("Control property values").subscribeOn(ConcurrentMainScheduler.instance) _valueSink = valueSink.asObserver() } public func on(event: Event<E>) { switch event { case .Error(let error): bindingErrorToInterface(error) case .Next: _valueSink.on(event) case .Completed: _valueSink.on(event) } } } 

La configuration de mi testing fue como sigue, he eliminado todas las vistas posicionadas aquí para hacerlo más corto:

 import UIKit import RxSwift import RxCocoa class ViewController: UIViewController { let variable = Variable<Bool>(false); let bag = DisposeBag(); override func loadView() { super.loadView() let aSwitch = UISwitch(); view.addSubview(aSwitch) (aSwitch.rx_value <-> variable).addDisposableTo(bag); let button = UIButton(); button.rx_tap.subscribeNext { [weak self] in self?.variable.value = true; }.addDisposableTo(bag) view.addSubview(button); } } infix operator <-> { } func <-> <T>(property: ControlProperty<T>, variable: Variable<T>) -> Disposable{ let bindToUIDisposable = variable.asObservable().debug("Variable values in bind") .bindTo(property) let bindToVariable = property .debug("Property values in bind") .subscribe(onNext: { n in variable.value = n }, onCompleted: { bindToUIDisposable.dispose() }) return StableCompositeDisposable.create(bindToUIDisposable, bindToVariable) } 

Ahora a los resultados. Primero intentamos tocar el button, que debe establecer la variable en true . Esto activa on(event: Event<E>) en ControlProperty y establece el valor del conmutador en true .

 2016-05-28 12:24:33.229: Variable values in bind -> Event Next(true) // value flow value assigned to Variable -> Variable emits event -> ControlProperty receives event -> value assigned to underlying control property (eg `on` for `UISwitch`) 

A continuación, activamos el interruptor en sí. Como podemos ver, el control generó un evento como resultado de UIControlEventValueChanged que se pasó a través de _values en ControlProperty, y luego su valor se asignó a Valor de Variable como en el ejemplo anterior. Pero no hay bucle, ya que la actualización del valor de la Variable no activa un evento de control en el conmutador.

 2016-05-28 12:29:01.957: Control property values -> Event Next(false) 2016-05-28 12:29:01.957: Property values in bind -> Event Next(false) 2016-05-28 12:29:01.958: Variable values in bind -> Event Next(false) // value flow trigger the state of control (eg `UISwitch`) -> ControlProperty emits event -> value assigned to Variable -> Variable emits event -> ControlProperty receives event -> value assigned to underlying control property (eg `on` for `UISwitch`) 

Entonces una simple explicación sería:

  • un valor de un control se emite una vez que se activa algún tipo de UIControlEvent
  • cuando un valor se asigna directamente a la propiedad de control, el control no dispara un evento de cambio, por lo que no hay bucle.

Espero que ayude, lo siento por una explicación un tanto desorderada – Lo he descubierto por experiencia)

Creo que puedes usar bindTo 🙂. Aquí hay implementaciones para ControlProperty <-> Variable y Variable <-> Variable :

 infix operator <-> { precedence 130 associativity left } func <-><T: Comparable>(property: ControlProperty<T>, variable: Variable<T>) -> Disposable { let variableToProperty = variable.asObservable() .distinctUntilChanged() .bindTo(property) let propertyToVariable = property .distinctUntilChanged() .bindTo(variable) return StableCompositeDisposable.create(variableToProperty, propertyToVariable) } func <-><T: Comparable>(left: Variable<T>, right: Variable<T>) -> Disposable { let leftToRight = left.asObservable() .distinctUntilChanged() .bindTo(right) let rightToLeft = right.asObservable() .distinctUntilChanged() .bindTo(left) return StableCompositeDisposable.create(leftToRight, rightToLeft) } 

Ejemplos de ControlProperty <-> Variable (como UITextField y UITextView ) están en el proyecto RxSwiftPlayer

 // Example of Variable <-> Variable let disposeBag = DisposeBag() let var1 = Variable(1) let var2 = Variable(2) (var1 <-> var2).addDisposableTo(disposeBag) var1.value = 10 print(var2.value) // 10 var2.value = 20 print(var1.value) // 20