Property.swift 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651
  1. import Foundation
  2. import enum Result.NoError
  3. /// Represents a property that allows observation of its changes.
  4. ///
  5. /// Only classes can conform to this protocol, because having a signal
  6. /// for changes over time implies the origin must have a unique identity.
  7. public protocol PropertyProtocol: class, BindingSourceProtocol {
  8. associatedtype Value
  9. /// The current value of the property.
  10. var value: Value { get }
  11. /// The values producer of the property.
  12. ///
  13. /// It produces a signal that sends the property's current value,
  14. /// followed by all changes over time. It completes when the property
  15. /// has deinitialized, or has no further change.
  16. ///
  17. /// - note: If `self` is a composed property, the producer would be
  18. /// bound to the lifetime of its sources.
  19. var producer: SignalProducer<Value, NoError> { get }
  20. /// A signal that will send the property's changes over time. It
  21. /// completes when the property has deinitialized, or has no further
  22. /// change.
  23. ///
  24. /// - note: If `self` is a composed property, the signal would be
  25. /// bound to the lifetime of its sources.
  26. var signal: Signal<Value, NoError> { get }
  27. }
  28. extension PropertyProtocol {
  29. @discardableResult
  30. public func observe(_ observer: Observer<Value, NoError>, during lifetime: Lifetime) -> Disposable? {
  31. return producer.observe(observer, during: lifetime)
  32. }
  33. }
  34. /// Represents an observable property that can be mutated directly.
  35. public protocol MutablePropertyProtocol: PropertyProtocol, BindingTargetProtocol {
  36. /// The current value of the property.
  37. var value: Value { get set }
  38. }
  39. /// Default implementation of `MutablePropertyProtocol` for `BindingTarget`.
  40. extension MutablePropertyProtocol {
  41. public func consume(_ value: Value) {
  42. self.value = value
  43. }
  44. }
  45. // Property operators.
  46. //
  47. // A composed property is a transformed view of its sources, and does not
  48. // own its lifetime. Its producer and signal are bound to the lifetime of
  49. // its sources.
  50. extension PropertyProtocol {
  51. /// Lifts a unary SignalProducer operator to operate upon PropertyProtocol instead.
  52. fileprivate func lift<U>(_ transform: @escaping (SignalProducer<Value, NoError>) -> SignalProducer<U, NoError>) -> Property<U> {
  53. return Property(self, transform: transform)
  54. }
  55. /// Lifts a binary SignalProducer operator to operate upon PropertyProtocol instead.
  56. fileprivate func lift<P: PropertyProtocol, U>(_ transform: @escaping (SignalProducer<Value, NoError>) -> (SignalProducer<P.Value, NoError>) -> SignalProducer<U, NoError>) -> (P) -> Property<U> {
  57. return { otherProperty in
  58. return Property(self, otherProperty, transform: transform)
  59. }
  60. }
  61. /// Maps the current value and all subsequent values to a new property.
  62. ///
  63. /// - parameters:
  64. /// - transform: A closure that will map the current `value` of this
  65. /// `Property` to a new value.
  66. ///
  67. /// - returns: A new instance of `AnyProperty` who's holds a mapped value
  68. /// from `self`.
  69. public func map<U>(_ transform: @escaping (Value) -> U) -> Property<U> {
  70. return lift { $0.map(transform) }
  71. }
  72. /// Combines the current value and the subsequent values of two `Property`s in
  73. /// the manner described by `Signal.combineLatestWith:`.
  74. ///
  75. /// - parameters:
  76. /// - other: A property to combine `self`'s value with.
  77. ///
  78. /// - returns: A property that holds a tuple containing values of `self` and
  79. /// the given property.
  80. public func combineLatest<P: PropertyProtocol>(with other: P) -> Property<(Value, P.Value)> {
  81. return lift(SignalProducer.combineLatest(with:))(other)
  82. }
  83. /// Zips the current value and the subsequent values of two `Property`s in
  84. /// the manner described by `Signal.zipWith`.
  85. ///
  86. /// - parameters:
  87. /// - other: A property to zip `self`'s value with.
  88. ///
  89. /// - returns: A property that holds a tuple containing values of `self` and
  90. /// the given property.
  91. public func zip<P: PropertyProtocol>(with other: P) -> Property<(Value, P.Value)> {
  92. return lift(SignalProducer.zip(with:))(other)
  93. }
  94. /// Forward events from `self` with history: values of the returned property
  95. /// are a tuple whose first member is the previous value and whose second
  96. /// member is the current value. `initial` is supplied as the first member
  97. /// when `self` sends its first value.
  98. ///
  99. /// - parameters:
  100. /// - initial: A value that will be combined with the first value sent by
  101. /// `self`.
  102. ///
  103. /// - returns: A property that holds tuples that contain previous and
  104. /// current values of `self`.
  105. public func combinePrevious(_ initial: Value) -> Property<(Value, Value)> {
  106. return lift { $0.combinePrevious(initial) }
  107. }
  108. /// Forward only those values from `self` which do not pass `isRepeat` with
  109. /// respect to the previous value.
  110. ///
  111. /// - parameters:
  112. /// - isRepeat: A predicate to determine if the two given values are equal.
  113. ///
  114. /// - returns: A property that does not emit events for two equal values
  115. /// sequentially.
  116. public func skipRepeats(_ isRepeat: @escaping (Value, Value) -> Bool) -> Property<Value> {
  117. return lift { $0.skipRepeats(isRepeat) }
  118. }
  119. }
  120. extension PropertyProtocol where Value: Equatable {
  121. /// Forward only those values from `self` which do not pass `isRepeat` with
  122. /// respect to the previous value.
  123. ///
  124. /// - returns: A property that does not emit events for two equal values
  125. /// sequentially.
  126. public func skipRepeats() -> Property<Value> {
  127. return lift { $0.skipRepeats() }
  128. }
  129. }
  130. extension PropertyProtocol where Value: PropertyProtocol {
  131. /// Flattens the inner property held by `self` (into a single property of
  132. /// values), according to the semantics of the given strategy.
  133. ///
  134. /// - parameters:
  135. /// - strategy: The preferred flatten strategy.
  136. ///
  137. /// - returns: A property that sends the values of its inner properties.
  138. public func flatten(_ strategy: FlattenStrategy) -> Property<Value.Value> {
  139. return lift { $0.flatMap(strategy) { $0.producer } }
  140. }
  141. }
  142. extension PropertyProtocol {
  143. /// Maps each property from `self` to a new property, then flattens the
  144. /// resulting properties (into a single property), according to the
  145. /// semantics of the given strategy.
  146. ///
  147. /// - parameters:
  148. /// - strategy: The preferred flatten strategy.
  149. /// - transform: The transform to be applied on `self` before flattening.
  150. ///
  151. /// - returns: A property that sends the values of its inner properties.
  152. public func flatMap<P: PropertyProtocol>(_ strategy: FlattenStrategy, transform: @escaping (Value) -> P) -> Property<P.Value> {
  153. return lift { $0.flatMap(strategy) { transform($0).producer } }
  154. }
  155. /// Forward only those values from `self` that have unique identities across
  156. /// the set of all values that have been held.
  157. ///
  158. /// - note: This causes the identities to be retained to check for
  159. /// uniqueness.
  160. ///
  161. /// - parameters:
  162. /// - transform: A closure that accepts a value and returns identity
  163. /// value.
  164. ///
  165. /// - returns: A property that sends unique values during its lifetime.
  166. public func uniqueValues<Identity: Hashable>(_ transform: @escaping (Value) -> Identity) -> Property<Value> {
  167. return lift { $0.uniqueValues(transform) }
  168. }
  169. }
  170. extension PropertyProtocol where Value: Hashable {
  171. /// Forwards only those values from `self` that are unique across the set of
  172. /// all values that have been seen.
  173. ///
  174. /// - note: This causes the identities to be retained to check for uniqueness.
  175. /// Providing a function that returns a unique value for each sent
  176. /// value can help you reduce the memory footprint.
  177. ///
  178. /// - returns: A property that sends unique values during its lifetime.
  179. public func uniqueValues() -> Property<Value> {
  180. return lift { $0.uniqueValues() }
  181. }
  182. }
  183. extension PropertyProtocol {
  184. /// Combines the values of all the given properties, in the manner described
  185. /// by `combineLatest(with:)`.
  186. public static func combineLatest<A: PropertyProtocol, B: PropertyProtocol>(_ a: A, _ b: B) -> Property<(A.Value, B.Value)> where Value == A.Value {
  187. return a.combineLatest(with: b)
  188. }
  189. /// Combines the values of all the given properties, in the manner described
  190. /// by `combineLatest(with:)`.
  191. public static func combineLatest<A: PropertyProtocol, B: PropertyProtocol, C: PropertyProtocol>(_ a: A, _ b: B, _ c: C) -> Property<(A.Value, B.Value, C.Value)> where Value == A.Value {
  192. return combineLatest(a, b)
  193. .combineLatest(with: c)
  194. .map(repack)
  195. }
  196. /// Combines the values of all the given properties, in the manner described
  197. /// by `combineLatest(with:)`.
  198. public static func combineLatest<A: PropertyProtocol, B: PropertyProtocol, C: PropertyProtocol, D: PropertyProtocol>(_ a: A, _ b: B, _ c: C, _ d: D) -> Property<(A.Value, B.Value, C.Value, D.Value)> where Value == A.Value {
  199. return combineLatest(a, b, c)
  200. .combineLatest(with: d)
  201. .map(repack)
  202. }
  203. /// Combines the values of all the given properties, in the manner described
  204. /// by `combineLatest(with:)`.
  205. public static func combineLatest<A: PropertyProtocol, B: PropertyProtocol, C: PropertyProtocol, D: PropertyProtocol, E: PropertyProtocol>(_ a: A, _ b: B, _ c: C, _ d: D, _ e: E) -> Property<(A.Value, B.Value, C.Value, D.Value, E.Value)> where Value == A.Value {
  206. return combineLatest(a, b, c, d)
  207. .combineLatest(with: e)
  208. .map(repack)
  209. }
  210. /// Combines the values of all the given properties, in the manner described
  211. /// by `combineLatest(with:)`.
  212. public static func combineLatest<A: PropertyProtocol, B: PropertyProtocol, C: PropertyProtocol, D: PropertyProtocol, E: PropertyProtocol, F: PropertyProtocol>(_ a: A, _ b: B, _ c: C, _ d: D, _ e: E, _ f: F) -> Property<(A.Value, B.Value, C.Value, D.Value, E.Value, F.Value)> where Value == A.Value {
  213. return combineLatest(a, b, c, d, e)
  214. .combineLatest(with: f)
  215. .map(repack)
  216. }
  217. /// Combines the values of all the given properties, in the manner described
  218. /// by `combineLatest(with:)`.
  219. public static func combineLatest<A: PropertyProtocol, B: PropertyProtocol, C: PropertyProtocol, D: PropertyProtocol, E: PropertyProtocol, F: PropertyProtocol, G: PropertyProtocol>(_ a: A, _ b: B, _ c: C, _ d: D, _ e: E, _ f: F, _ g: G) -> Property<(A.Value, B.Value, C.Value, D.Value, E.Value, F.Value, G.Value)> where Value == A.Value {
  220. return combineLatest(a, b, c, d, e, f)
  221. .combineLatest(with: g)
  222. .map(repack)
  223. }
  224. /// Combines the values of all the given properties, in the manner described
  225. /// by `combineLatest(with:)`.
  226. public static func combineLatest<A: PropertyProtocol, B: PropertyProtocol, C: PropertyProtocol, D: PropertyProtocol, E: PropertyProtocol, F: PropertyProtocol, G: PropertyProtocol, H: PropertyProtocol>(_ a: A, _ b: B, _ c: C, _ d: D, _ e: E, _ f: F, _ g: G, _ h: H) -> Property<(A.Value, B.Value, C.Value, D.Value, E.Value, F.Value, G.Value, H.Value)> where Value == A.Value {
  227. return combineLatest(a, b, c, d, e, f, g)
  228. .combineLatest(with: h)
  229. .map(repack)
  230. }
  231. /// Combines the values of all the given properties, in the manner described
  232. /// by `combineLatest(with:)`.
  233. public static func combineLatest<A: PropertyProtocol, B: PropertyProtocol, C: PropertyProtocol, D: PropertyProtocol, E: PropertyProtocol, F: PropertyProtocol, G: PropertyProtocol, H: PropertyProtocol, I: PropertyProtocol>(_ a: A, _ b: B, _ c: C, _ d: D, _ e: E, _ f: F, _ g: G, _ h: H, _ i: I) -> Property<(A.Value, B.Value, C.Value, D.Value, E.Value, F.Value, G.Value, H.Value, I.Value)> where Value == A.Value {
  234. return combineLatest(a, b, c, d, e, f, g, h)
  235. .combineLatest(with: i)
  236. .map(repack)
  237. }
  238. /// Combines the values of all the given properties, in the manner described
  239. /// by `combineLatest(with:)`.
  240. public static func combineLatest<A: PropertyProtocol, B: PropertyProtocol, C: PropertyProtocol, D: PropertyProtocol, E: PropertyProtocol, F: PropertyProtocol, G: PropertyProtocol, H: PropertyProtocol, I: PropertyProtocol, J: PropertyProtocol>(_ a: A, _ b: B, _ c: C, _ d: D, _ e: E, _ f: F, _ g: G, _ h: H, _ i: I, _ j: J) -> Property<(A.Value, B.Value, C.Value, D.Value, E.Value, F.Value, G.Value, H.Value, I.Value, J.Value)> where Value == A.Value {
  241. return combineLatest(a, b, c, d, e, f, g, h, i)
  242. .combineLatest(with: j)
  243. .map(repack)
  244. }
  245. /// Combines the values of all the given producers, in the manner described by
  246. /// `combineLatest(with:)`. Returns nil if the sequence is empty.
  247. public static func combineLatest<S: Sequence>(_ properties: S) -> Property<[S.Iterator.Element.Value]>? where S.Iterator.Element: PropertyProtocol {
  248. var generator = properties.makeIterator()
  249. if let first = generator.next() {
  250. let initial = first.map { [$0] }
  251. return IteratorSequence(generator).reduce(initial) { property, next in
  252. property.combineLatest(with: next).map { $0.0 + [$0.1] }
  253. }
  254. }
  255. return nil
  256. }
  257. /// Zips the values of all the given properties, in the manner described by
  258. /// `zip(with:)`.
  259. public static func zip<A: PropertyProtocol, B: PropertyProtocol>(_ a: A, _ b: B) -> Property<(A.Value, B.Value)> where Value == A.Value {
  260. return a.zip(with: b)
  261. }
  262. /// Zips the values of all the given properties, in the manner described by
  263. /// `zip(with:)`.
  264. public static func zip<A: PropertyProtocol, B: PropertyProtocol, C: PropertyProtocol>(_ a: A, _ b: B, _ c: C) -> Property<(A.Value, B.Value, C.Value)> where Value == A.Value {
  265. return zip(a, b)
  266. .zip(with: c)
  267. .map(repack)
  268. }
  269. /// Zips the values of all the given properties, in the manner described by
  270. /// `zip(with:)`.
  271. public static func zip<A: PropertyProtocol, B: PropertyProtocol, C: PropertyProtocol, D: PropertyProtocol>(_ a: A, _ b: B, _ c: C, _ d: D) -> Property<(A.Value, B.Value, C.Value, D.Value)> where Value == A.Value {
  272. return zip(a, b, c)
  273. .zip(with: d)
  274. .map(repack)
  275. }
  276. /// Zips the values of all the given properties, in the manner described by
  277. /// `zip(with:)`.
  278. public static func zip<A: PropertyProtocol, B: PropertyProtocol, C: PropertyProtocol, D: PropertyProtocol, E: PropertyProtocol>(_ a: A, _ b: B, _ c: C, _ d: D, _ e: E) -> Property<(A.Value, B.Value, C.Value, D.Value, E.Value)> where Value == A.Value {
  279. return zip(a, b, c, d)
  280. .zip(with: e)
  281. .map(repack)
  282. }
  283. /// Zips the values of all the given properties, in the manner described by
  284. /// `zip(with:)`.
  285. public static func zip<A: PropertyProtocol, B: PropertyProtocol, C: PropertyProtocol, D: PropertyProtocol, E: PropertyProtocol, F: PropertyProtocol>(_ a: A, _ b: B, _ c: C, _ d: D, _ e: E, _ f: F) -> Property<(A.Value, B.Value, C.Value, D.Value, E.Value, F.Value)> where Value == A.Value {
  286. return zip(a, b, c, d, e)
  287. .zip(with: f)
  288. .map(repack)
  289. }
  290. /// Zips the values of all the given properties, in the manner described by
  291. /// `zip(with:)`.
  292. public static func zip<A: PropertyProtocol, B: PropertyProtocol, C: PropertyProtocol, D: PropertyProtocol, E: PropertyProtocol, F: PropertyProtocol, G: PropertyProtocol>(_ a: A, _ b: B, _ c: C, _ d: D, _ e: E, _ f: F, _ g: G) -> Property<(A.Value, B.Value, C.Value, D.Value, E.Value, F.Value, G.Value)> where Value == A.Value {
  293. return zip(a, b, c, d, e, f)
  294. .zip(with: g)
  295. .map(repack)
  296. }
  297. /// Zips the values of all the given properties, in the manner described by
  298. /// `zip(with:)`.
  299. public static func zip<A: PropertyProtocol, B: PropertyProtocol, C: PropertyProtocol, D: PropertyProtocol, E: PropertyProtocol, F: PropertyProtocol, G: PropertyProtocol, H: PropertyProtocol>(_ a: A, _ b: B, _ c: C, _ d: D, _ e: E, _ f: F, _ g: G, _ h: H) -> Property<(A.Value, B.Value, C.Value, D.Value, E.Value, F.Value, G.Value, H.Value)> where Value == A.Value {
  300. return zip(a, b, c, d, e, f, g)
  301. .zip(with: h)
  302. .map(repack)
  303. }
  304. /// Zips the values of all the given properties, in the manner described by
  305. /// `zip(with:)`.
  306. public static func zip<A: PropertyProtocol, B: PropertyProtocol, C: PropertyProtocol, D: PropertyProtocol, E: PropertyProtocol, F: PropertyProtocol, G: PropertyProtocol, H: PropertyProtocol, I: PropertyProtocol>(_ a: A, _ b: B, _ c: C, _ d: D, _ e: E, _ f: F, _ g: G, _ h: H, _ i: I) -> Property<(A.Value, B.Value, C.Value, D.Value, E.Value, F.Value, G.Value, H.Value, I.Value)> where Value == A.Value {
  307. return zip(a, b, c, d, e, f, g, h)
  308. .zip(with: i)
  309. .map(repack)
  310. }
  311. /// Zips the values of all the given properties, in the manner described by
  312. /// `zip(with:)`.
  313. public static func zip<A: PropertyProtocol, B: PropertyProtocol, C: PropertyProtocol, D: PropertyProtocol, E: PropertyProtocol, F: PropertyProtocol, G: PropertyProtocol, H: PropertyProtocol, I: PropertyProtocol, J: PropertyProtocol>(_ a: A, _ b: B, _ c: C, _ d: D, _ e: E, _ f: F, _ g: G, _ h: H, _ i: I, _ j: J) -> Property<(A.Value, B.Value, C.Value, D.Value, E.Value, F.Value, G.Value, H.Value, I.Value, J.Value)> where Value == A.Value {
  314. return zip(a, b, c, d, e, f, g, h, i)
  315. .zip(with: j)
  316. .map(repack)
  317. }
  318. /// Zips the values of all the given properties, in the manner described by
  319. /// `zip(with:)`. Returns nil if the sequence is empty.
  320. public static func zip<S: Sequence>(_ properties: S) -> Property<[S.Iterator.Element.Value]>? where S.Iterator.Element: PropertyProtocol {
  321. var generator = properties.makeIterator()
  322. if let first = generator.next() {
  323. let initial = first.map { [$0] }
  324. return IteratorSequence(generator).reduce(initial) { property, next in
  325. property.zip(with: next).map { $0.0 + [$0.1] }
  326. }
  327. }
  328. return nil
  329. }
  330. }
  331. /// A read-only property that can be observed for its changes over time. There
  332. /// are three categories of read-only properties:
  333. ///
  334. /// # Constant property
  335. /// Created by `Property(value:)`, the producer and signal of a constant
  336. /// property would complete immediately when it is initialized.
  337. ///
  338. /// # Existential property
  339. /// Created by `Property(capturing:)`, it wraps any arbitrary `PropertyProtocol`
  340. /// types, and passes through the behavior. Note that it would retain the
  341. /// wrapped property.
  342. ///
  343. /// Existential property would be deprecated when generalized existential
  344. /// eventually lands in Swift.
  345. ///
  346. /// # Composed property
  347. /// A composed property presents a composed view of its sources, which can be
  348. /// one or more properties, a producer, or a signal. It can be created using
  349. /// property composition operators, `Property(_:)` or `Property(initial:then:)`.
  350. ///
  351. /// It does not own its lifetime, and its producer and signal are bound to the
  352. /// lifetime of its sources. It also does not have an influence on its sources,
  353. /// so retaining a composed property would not prevent its sources from
  354. /// deinitializing.
  355. ///
  356. /// Note that composed properties do not retain any of its sources.
  357. public final class Property<Value>: PropertyProtocol {
  358. private let disposable: Disposable?
  359. private let _value: () -> Value
  360. private let _producer: () -> SignalProducer<Value, NoError>
  361. private let _signal: () -> Signal<Value, NoError>
  362. /// The current value of the property.
  363. public var value: Value {
  364. return _value()
  365. }
  366. /// A producer for Signals that will send the property's current
  367. /// value, followed by all changes over time, then complete when the
  368. /// property has deinitialized or has no further changes.
  369. ///
  370. /// - note: If `self` is a composed property, the producer would be
  371. /// bound to the lifetime of its sources.
  372. public var producer: SignalProducer<Value, NoError> {
  373. return _producer()
  374. }
  375. /// A signal that will send the property's changes over time, then
  376. /// complete when the property has deinitialized or has no further changes.
  377. ///
  378. /// - note: If `self` is a composed property, the signal would be
  379. /// bound to the lifetime of its sources.
  380. public var signal: Signal<Value, NoError> {
  381. return _signal()
  382. }
  383. /// Initializes a constant property.
  384. ///
  385. /// - parameters:
  386. /// - property: A value of the constant property.
  387. public init(value: Value) {
  388. disposable = nil
  389. _value = { value }
  390. _producer = { SignalProducer(value: value) }
  391. _signal = { Signal<Value, NoError>.empty }
  392. }
  393. /// Initializes an existential property which wraps the given property.
  394. ///
  395. /// - note: The resulting property retains the given property.
  396. ///
  397. /// - parameters:
  398. /// - property: A property to be wrapped.
  399. public init<P: PropertyProtocol>(capturing property: P) where P.Value == Value {
  400. disposable = nil
  401. _value = { property.value }
  402. _producer = { property.producer }
  403. _signal = { property.signal }
  404. }
  405. /// Initializes a composed property which reflects the given property.
  406. ///
  407. /// - note: The resulting property does not retain the given property.
  408. ///
  409. /// - parameters:
  410. /// - property: A property to be wrapped.
  411. public convenience init<P: PropertyProtocol>(_ property: P) where P.Value == Value {
  412. self.init(unsafeProducer: property.producer)
  413. }
  414. /// Initializes a composed property that first takes on `initial`, then each
  415. /// value sent on a signal created by `producer`.
  416. ///
  417. /// - parameters:
  418. /// - initial: Starting value for the property.
  419. /// - values: A producer that will start immediately and send values to
  420. /// the property.
  421. public convenience init(initial: Value, then values: SignalProducer<Value, NoError>) {
  422. self.init(unsafeProducer: values.prefix(value: initial))
  423. }
  424. /// Initialize a composed property that first takes on `initial`, then each
  425. /// value sent on `signal`.
  426. ///
  427. /// - parameters:
  428. /// - initialValue: Starting value for the property.
  429. /// - values: A signal that will send values to the property.
  430. public convenience init(initial: Value, then values: Signal<Value, NoError>) {
  431. self.init(unsafeProducer: SignalProducer(values).prefix(value: initial))
  432. }
  433. /// Initialize a composed property by applying the unary `SignalProducer`
  434. /// transform on `property`.
  435. ///
  436. /// - parameters:
  437. /// - property: The source property.
  438. /// - transform: A unary `SignalProducer` transform to be applied on
  439. /// `property`.
  440. fileprivate convenience init<P: PropertyProtocol>(
  441. _ property: P,
  442. transform: @escaping (SignalProducer<P.Value, NoError>) -> SignalProducer<Value, NoError>
  443. ) {
  444. self.init(unsafeProducer: transform(property.producer))
  445. }
  446. /// Initialize a composed property by applying the binary `SignalProducer`
  447. /// transform on `firstProperty` and `secondProperty`.
  448. ///
  449. /// - parameters:
  450. /// - firstProperty: The first source property.
  451. /// - secondProperty: The first source property.
  452. /// - transform: A binary `SignalProducer` transform to be applied on
  453. /// `firstProperty` and `secondProperty`.
  454. fileprivate convenience init<P1: PropertyProtocol, P2: PropertyProtocol>(_ firstProperty: P1, _ secondProperty: P2, transform: @escaping (SignalProducer<P1.Value, NoError>) -> (SignalProducer<P2.Value, NoError>) -> SignalProducer<Value, NoError>) {
  455. self.init(unsafeProducer: transform(firstProperty.producer)(secondProperty.producer))
  456. }
  457. /// Initialize a composed property from a producer that promises to send
  458. /// at least one value synchronously in its start handler before sending any
  459. /// subsequent event.
  460. ///
  461. /// - important: The producer and the signal of the created property would
  462. /// complete only when the `unsafeProducer` completes.
  463. ///
  464. /// - warning: If the producer fails its promise, a fatal error would be
  465. /// raised.
  466. ///
  467. /// - parameters:
  468. /// - unsafeProducer: The composed producer for creating the property.
  469. private init(unsafeProducer: SignalProducer<Value, NoError>) {
  470. // Share a replayed producer with `self.producer` and `self.signal` so
  471. // they see a consistent view of the `self.value`.
  472. // https://github.com/ReactiveCocoa/ReactiveCocoa/pull/3042
  473. let producer = unsafeProducer.replayLazily(upTo: 1)
  474. let atomic = Atomic<Value?>(nil)
  475. disposable = producer.startWithValues { atomic.value = $0 }
  476. // Verify that an initial is sent. This is friendlier than deadlocking
  477. // in the event that one isn't.
  478. guard atomic.value != nil else {
  479. fatalError("A producer promised to send at least one value. Received none.")
  480. }
  481. _value = { atomic.value! }
  482. _producer = { producer }
  483. _signal = { producer.startAndRetrieveSignal() }
  484. }
  485. deinit {
  486. disposable?.dispose()
  487. }
  488. }
  489. /// A mutable property of type `Value` that allows observation of its changes.
  490. ///
  491. /// Instances of this class are thread-safe.
  492. public final class MutableProperty<Value>: MutablePropertyProtocol {
  493. private let token: Lifetime.Token
  494. private let observer: Signal<Value, NoError>.Observer
  495. private let atomic: RecursiveAtomic<Value>
  496. /// The current value of the property.
  497. ///
  498. /// Setting this to a new value will notify all observers of `signal`, or
  499. /// signals created using `producer`.
  500. public var value: Value {
  501. get {
  502. return atomic.withValue { $0 }
  503. }
  504. set {
  505. swap(newValue)
  506. }
  507. }
  508. /// The lifetime of the property.
  509. public let lifetime: Lifetime
  510. /// A signal that will send the property's changes over time,
  511. /// then complete when the property has deinitialized.
  512. public let signal: Signal<Value, NoError>
  513. /// A producer for Signals that will send the property's current value,
  514. /// followed by all changes over time, then complete when the property has
  515. /// deinitialized.
  516. public var producer: SignalProducer<Value, NoError> {
  517. return SignalProducer { [atomic, weak self] producerObserver, producerDisposable in
  518. atomic.withValue { value in
  519. if let strongSelf = self {
  520. producerObserver.send(value: value)
  521. producerDisposable += strongSelf.signal.observe(producerObserver)
  522. } else {
  523. producerObserver.send(value: value)
  524. producerObserver.sendCompleted()
  525. }
  526. }
  527. }
  528. }
  529. /// Initializes a mutable property that first takes on `initialValue`
  530. ///
  531. /// - parameters:
  532. /// - initialValue: Starting value for the mutable property.
  533. public init(_ initialValue: Value) {
  534. (signal, observer) = Signal.pipe()
  535. token = Lifetime.Token()
  536. lifetime = Lifetime(token)
  537. /// Need a recursive lock around `value` to allow recursive access to
  538. /// `value`. Note that recursive sets will still deadlock because the
  539. /// underlying producer prevents sending recursive events.
  540. atomic = RecursiveAtomic(initialValue,
  541. name: "org.reactivecocoa.ReactiveSwift.MutableProperty",
  542. didSet: observer.send(value:))
  543. }
  544. /// Atomically replaces the contents of the variable.
  545. ///
  546. /// - parameters:
  547. /// - newValue: New property value.
  548. ///
  549. /// - returns: The previous property value.
  550. @discardableResult
  551. public func swap(_ newValue: Value) -> Value {
  552. return atomic.swap(newValue)
  553. }
  554. /// Atomically modifies the variable.
  555. ///
  556. /// - parameters:
  557. /// - action: A closure that accepts old property value and returns a new
  558. /// property value.
  559. ///
  560. /// - returns: The result of the action.
  561. @discardableResult
  562. public func modify<Result>(_ action: (inout Value) throws -> Result) rethrows -> Result {
  563. return try atomic.modify(action)
  564. }
  565. /// Atomically performs an arbitrary action using the current value of the
  566. /// variable.
  567. ///
  568. /// - parameters:
  569. /// - action: A closure that accepts current property value.
  570. ///
  571. /// - returns: the result of the action.
  572. @discardableResult
  573. public func withValue<Result>(action: (Value) throws -> Result) rethrows -> Result {
  574. return try atomic.withValue(action)
  575. }
  576. deinit {
  577. observer.sendCompleted()
  578. }
  579. }