UnidirectionalBinding.swift 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. import Foundation
  2. import Dispatch
  3. import enum Result.NoError
  4. precedencegroup BindingPrecedence {
  5. associativity: right
  6. // Binds tighter than assignment but looser than everything else
  7. higherThan: AssignmentPrecedence
  8. }
  9. infix operator <~ : BindingPrecedence
  10. /// Describes a source which can be bound.
  11. public protocol BindingSourceProtocol {
  12. associatedtype Value
  13. associatedtype Error: Swift.Error
  14. /// Observe the binding source by sending any events to the given observer.
  15. @discardableResult
  16. func observe(_ observer: Observer<Value, Error>, during lifetime: Lifetime) -> Disposable?
  17. }
  18. extension Signal: BindingSourceProtocol {
  19. @discardableResult
  20. public func observe(_ observer: Observer, during lifetime: Lifetime) -> Disposable? {
  21. return self.take(during: lifetime).observe(observer)
  22. }
  23. }
  24. extension SignalProducer: BindingSourceProtocol {
  25. @discardableResult
  26. public func observe(_ observer: ProducedSignal.Observer, during lifetime: Lifetime) -> Disposable? {
  27. var disposable: Disposable!
  28. self
  29. .take(during: lifetime)
  30. .startWithSignal { signal, signalDisposable in
  31. disposable = signalDisposable
  32. signal.observe(observer)
  33. }
  34. return disposable
  35. }
  36. }
  37. /// Describes a target which can be bound.
  38. public protocol BindingTargetProtocol: class {
  39. associatedtype Value
  40. /// The lifetime of `self`. The binding operators use this to determine when
  41. /// the binding should be torn down.
  42. var lifetime: Lifetime { get }
  43. /// Consume a value from the binding.
  44. func consume(_ value: Value)
  45. }
  46. /// Binds a source to a target, updating the target's value to the latest
  47. /// value sent by the source.
  48. ///
  49. /// - note: The binding will automatically terminate when the target is
  50. /// deinitialized, or when the source sends a `completed` event.
  51. ///
  52. /// ````
  53. /// let property = MutableProperty(0)
  54. /// let signal = Signal({ /* do some work after some time */ })
  55. /// property <~ signal
  56. /// ````
  57. ///
  58. /// ````
  59. /// let property = MutableProperty(0)
  60. /// let signal = Signal({ /* do some work after some time */ })
  61. /// let disposable = property <~ signal
  62. /// ...
  63. /// // Terminates binding before property dealloc or signal's
  64. /// // `completed` event.
  65. /// disposable.dispose()
  66. /// ````
  67. ///
  68. /// - parameters:
  69. /// - target: A target to be bond to.
  70. /// - source: A source to bind.
  71. ///
  72. /// - returns: A disposable that can be used to terminate binding before the
  73. /// deinitialization of the target or the source's `completed`
  74. /// event.
  75. @discardableResult
  76. public func <~
  77. <Target: BindingTargetProtocol, Source: BindingSourceProtocol>
  78. (target: Target, source: Source) -> Disposable?
  79. where Source.Value == Target.Value, Source.Error == NoError
  80. {
  81. // Alter the semantics of `BindingTarget` to not require it to be retained.
  82. // This is done here--and not in a separate function--so that all variants
  83. // of `<~` can get this behavior.
  84. let observer: Observer<Target.Value, NoError>
  85. if let target = target as? BindingTarget<Target.Value> {
  86. observer = Observer(value: { [setter = target.setter] in setter($0) })
  87. } else {
  88. observer = Observer(value: { [weak target] in target?.consume($0) })
  89. }
  90. return source.observe(observer, during: target.lifetime)
  91. }
  92. /// Binds a source to a target, updating the target's value to the latest
  93. /// value sent by the source.
  94. ///
  95. /// - note: The binding will automatically terminate when the target is
  96. /// deinitialized, or when the source sends a `completed` event.
  97. ///
  98. /// ````
  99. /// let property = MutableProperty(0)
  100. /// let signal = Signal({ /* do some work after some time */ })
  101. /// property <~ signal
  102. /// ````
  103. ///
  104. /// ````
  105. /// let property = MutableProperty(0)
  106. /// let signal = Signal({ /* do some work after some time */ })
  107. /// let disposable = property <~ signal
  108. /// ...
  109. /// // Terminates binding before property dealloc or signal's
  110. /// // `completed` event.
  111. /// disposable.dispose()
  112. /// ````
  113. ///
  114. /// - parameters:
  115. /// - target: A target to be bond to.
  116. /// - source: A source to bind.
  117. ///
  118. /// - returns: A disposable that can be used to terminate binding before the
  119. /// deinitialization of the target or the source's `completed`
  120. /// event.
  121. @discardableResult
  122. public func <~
  123. <Target: BindingTargetProtocol, Source: BindingSourceProtocol>
  124. (target: Target, source: Source) -> Disposable?
  125. where Target.Value: OptionalProtocol, Source.Value == Target.Value.Wrapped, Source.Error == NoError
  126. {
  127. // Alter the semantics of `BindingTarget` to not require it to be retained.
  128. // This is done here--and not in a separate function--so that all variants
  129. // of `<~` can get this behavior.
  130. let observer: Observer<Source.Value, NoError>
  131. if let target = target as? BindingTarget<Target.Value> {
  132. observer = Observer(value: { [setter = target.setter] in setter(Target.Value(reconstructing: $0)) })
  133. } else {
  134. observer = Observer(value: { [weak target] in target?.consume(Target.Value(reconstructing: $0)) })
  135. }
  136. return source.observe(observer, during: target.lifetime)
  137. }
  138. /// A binding target that can be used with the `<~` operator.
  139. public final class BindingTarget<Value>: BindingTargetProtocol {
  140. public let lifetime: Lifetime
  141. fileprivate let setter: (Value) -> Void
  142. /// Creates a binding target.
  143. ///
  144. /// - parameters:
  145. /// - lifetime: The expected lifetime of any bindings towards `self`.
  146. /// - setter: The action to consume values.
  147. public init(lifetime: Lifetime, setter: @escaping (Value) -> Void) {
  148. self.setter = setter
  149. self.lifetime = lifetime
  150. }
  151. /// Creates a binding target which consumes values on the specified scheduler.
  152. ///
  153. /// - parameters:
  154. /// - scheduler: The scheduler on which the `setter` consumes the values.
  155. /// - lifetime: The expected lifetime of any bindings towards `self`.
  156. /// - setter: The action to consume values.
  157. public convenience init(on scheduler: SchedulerProtocol, lifetime: Lifetime, setter: @escaping (Value) -> Void) {
  158. let setter: (Value) -> Void = { value in
  159. scheduler.schedule {
  160. setter(value)
  161. }
  162. }
  163. self.init(lifetime: lifetime, setter: setter)
  164. }
  165. public func consume(_ value: Value) {
  166. setter(value)
  167. }
  168. }