RACStream.h 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320
  1. //
  2. // RACStream.h
  3. // ReactiveCocoa
  4. //
  5. // Created by Justin Spahr-Summers on 2012-10-31.
  6. // Copyright (c) 2012 GitHub, Inc. All rights reserved.
  7. //
  8. #import <Foundation/Foundation.h>
  9. @class RACStream;
  10. /// A block which accepts a value from a RACStream and returns a new instance
  11. /// of the same stream class.
  12. ///
  13. /// Setting `stop` to `YES` will cause the bind to terminate after the returned
  14. /// value. Returning `nil` will result in immediate termination.
  15. typedef RACStream * (^RACStreamBindBlock)(id value, BOOL *stop);
  16. /// An abstract class representing any stream of values.
  17. ///
  18. /// This class represents a monad, upon which many stream-based operations can
  19. /// be built.
  20. ///
  21. /// When subclassing RACStream, only the methods in the main @interface body need
  22. /// to be overridden.
  23. @interface RACStream : NSObject
  24. /// Returns an empty stream.
  25. + (instancetype)empty;
  26. /// Lifts `value` into the stream monad.
  27. ///
  28. /// Returns a stream containing only the given value.
  29. + (instancetype)return:(id)value;
  30. /// Lazily binds a block to the values in the receiver.
  31. ///
  32. /// This should only be used if you need to terminate the bind early, or close
  33. /// over some state. -flattenMap: is more appropriate for all other cases.
  34. ///
  35. /// block - A block returning a RACStreamBindBlock. This block will be invoked
  36. /// each time the bound stream is re-evaluated. This block must not be
  37. /// nil or return nil.
  38. ///
  39. /// Returns a new stream which represents the combined result of all lazy
  40. /// applications of `block`.
  41. - (instancetype)bind:(RACStreamBindBlock (^)(void))block;
  42. /// Appends the values of `stream` to the values in the receiver.
  43. ///
  44. /// stream - A stream to concatenate. This must be an instance of the same
  45. /// concrete class as the receiver, and should not be `nil`.
  46. ///
  47. /// Returns a new stream representing the receiver followed by `stream`.
  48. - (instancetype)concat:(RACStream *)stream;
  49. /// Zips the values in the receiver with those of the given stream to create
  50. /// RACTuples.
  51. ///
  52. /// The first value of each stream will be combined, then the second value, and
  53. /// so forth, until at least one of the streams is exhausted.
  54. ///
  55. /// stream - The stream to zip with. This must be an instance of the same
  56. /// concrete class as the receiver, and should not be `nil`.
  57. ///
  58. /// Returns a new stream of RACTuples, representing the zipped values of the
  59. /// two streams.
  60. - (instancetype)zipWith:(RACStream *)stream;
  61. @end
  62. /// This extension contains functionality to support naming streams for
  63. /// debugging.
  64. ///
  65. /// Subclasses do not need to override the methods here.
  66. @interface RACStream ()
  67. /// The name of the stream. This is for debugging/human purposes only.
  68. @property (copy) NSString *name;
  69. /// Sets the name of the receiver to the given format string.
  70. ///
  71. /// This is for debugging purposes only, and won't do anything unless the DEBUG
  72. /// preprocessor macro is defined.
  73. ///
  74. /// Returns the receiver, for easy method chaining.
  75. - (instancetype)setNameWithFormat:(NSString *)format, ... NS_FORMAT_FUNCTION(1, 2);
  76. @end
  77. /// Operations built on the RACStream primitives.
  78. ///
  79. /// These methods do not need to be overridden, although subclasses may
  80. /// occasionally gain better performance from doing so.
  81. @interface RACStream (Operations)
  82. /// Maps `block` across the values in the receiver and flattens the result.
  83. ///
  84. /// Note that operators applied _after_ -flattenMap: behave differently from
  85. /// operators _within_ -flattenMap:. See the Examples section below.
  86. ///
  87. /// This corresponds to the `SelectMany` method in Rx.
  88. ///
  89. /// block - A block which accepts the values in the receiver and returns a new
  90. /// instance of the receiver's class. This block should not return `nil`.
  91. ///
  92. /// Examples
  93. ///
  94. /// [signal flattenMap:^(id x) {
  95. /// // Logs each time a returned signal completes.
  96. /// return [[RACSignal return:x] logCompleted];
  97. /// }];
  98. ///
  99. /// [[signal
  100. /// flattenMap:^(id x) {
  101. /// return [RACSignal return:x];
  102. /// }]
  103. /// // Logs only once, when all of the signals complete.
  104. /// logCompleted];
  105. ///
  106. /// Returns a new stream which represents the combined streams resulting from
  107. /// mapping `block`.
  108. - (instancetype)flattenMap:(RACStream * (^)(id value))block;
  109. /// Flattens a stream of streams.
  110. ///
  111. /// This corresponds to the `Merge` method in Rx.
  112. ///
  113. /// Returns a stream consisting of the combined streams obtained from the
  114. /// receiver.
  115. - (instancetype)flatten;
  116. /// Maps `block` across the values in the receiver.
  117. ///
  118. /// This corresponds to the `Select` method in Rx.
  119. ///
  120. /// Returns a new stream with the mapped values.
  121. - (instancetype)map:(id (^)(id value))block;
  122. /// Replace each value in the receiver with the given object.
  123. ///
  124. /// Returns a new stream which includes the given object once for each value in
  125. /// the receiver.
  126. - (instancetype)mapReplace:(id)object;
  127. /// Filters out values in the receiver that don't pass the given test.
  128. ///
  129. /// This corresponds to the `Where` method in Rx.
  130. ///
  131. /// Returns a new stream with only those values that passed.
  132. - (instancetype)filter:(BOOL (^)(id value))block;
  133. /// Filters out values in the receiver that equal (via -isEqual:) the provided value.
  134. ///
  135. /// value - The value can be `nil`, in which case it ignores `nil` values.
  136. ///
  137. /// Returns a new stream containing only the values which did not compare equal
  138. /// to `value`.
  139. - (instancetype)ignore:(id)value;
  140. /// Unpacks each RACTuple in the receiver and maps the values to a new value.
  141. ///
  142. /// reduceBlock - The block which reduces each RACTuple's values into one value.
  143. /// It must take as many arguments as the number of tuple elements
  144. /// to process. Each argument will be an object argument. The
  145. /// return value must be an object. This argument cannot be nil.
  146. ///
  147. /// Returns a new stream of reduced tuple values.
  148. - (instancetype)reduceEach:(id (^)())reduceBlock;
  149. /// Returns a stream consisting of `value`, followed by the values in the
  150. /// receiver.
  151. - (instancetype)startWith:(id)value;
  152. /// Skips the first `skipCount` values in the receiver.
  153. ///
  154. /// Returns the receiver after skipping the first `skipCount` values. If
  155. /// `skipCount` is greater than the number of values in the stream, an empty
  156. /// stream is returned.
  157. - (instancetype)skip:(NSUInteger)skipCount;
  158. /// Returns a stream of the first `count` values in the receiver. If `count` is
  159. /// greater than or equal to the number of values in the stream, a stream
  160. /// equivalent to the receiver is returned.
  161. - (instancetype)take:(NSUInteger)count;
  162. /// Zips the values in the given streams to create RACTuples.
  163. ///
  164. /// The first value of each stream will be combined, then the second value, and
  165. /// so forth, until at least one of the streams is exhausted.
  166. ///
  167. /// streams - The streams to combine. These must all be instances of the same
  168. /// concrete class implementing the protocol. If this collection is
  169. /// empty, the returned stream will be empty.
  170. ///
  171. /// Returns a new stream containing RACTuples of the zipped values from the
  172. /// streams.
  173. + (instancetype)zip:(id<NSFastEnumeration>)streams;
  174. /// Zips streams using +zip:, then reduces the resulting tuples into a single
  175. /// value using -reduceEach:
  176. ///
  177. /// streams - The streams to combine. These must all be instances of the
  178. /// same concrete class implementing the protocol. If this
  179. /// collection is empty, the returned stream will be empty.
  180. /// reduceBlock - The block which reduces the values from all the streams
  181. /// into one value. It must take as many arguments as the
  182. /// number of streams given. Each argument will be an object
  183. /// argument. The return value must be an object. This argument
  184. /// must not be nil.
  185. ///
  186. /// Example:
  187. ///
  188. /// [RACStream zip:@[ stringSignal, intSignal ] reduce:^(NSString *string, NSNumber *number) {
  189. /// return [NSString stringWithFormat:@"%@: %@", string, number];
  190. /// }];
  191. ///
  192. /// Returns a new stream containing the results from each invocation of
  193. /// `reduceBlock`.
  194. + (instancetype)zip:(id<NSFastEnumeration>)streams reduce:(id (^)())reduceBlock;
  195. /// Returns a stream obtained by concatenating `streams` in order.
  196. + (instancetype)concat:(id<NSFastEnumeration>)streams;
  197. /// Combines values in the receiver from left to right using the given block.
  198. ///
  199. /// The algorithm proceeds as follows:
  200. ///
  201. /// 1. `startingValue` is passed into the block as the `running` value, and the
  202. /// first element of the receiver is passed into the block as the `next` value.
  203. /// 2. The result of the invocation is added to the returned stream.
  204. /// 3. The result of the invocation (`running`) and the next element of the
  205. /// receiver (`next`) is passed into `block`.
  206. /// 4. Steps 2 and 3 are repeated until all values have been processed.
  207. ///
  208. /// startingValue - The value to be combined with the first element of the
  209. /// receiver. This value may be `nil`.
  210. /// block - A block that describes how to combine values of the
  211. /// receiver. If the receiver is empty, this block will never be
  212. /// invoked.
  213. ///
  214. /// Examples
  215. ///
  216. /// RACSequence *numbers = @[ @1, @2, @3, @4 ].rac_sequence;
  217. ///
  218. /// // Contains 1, 3, 6, 10
  219. /// RACSequence *sums = [numbers scanWithStart:@0 reduce:^(NSNumber *sum, NSNumber *next) {
  220. /// return @(sum.integerValue + next.integerValue);
  221. /// }];
  222. ///
  223. /// Returns a new stream that consists of each application of `block`. If the
  224. /// receiver is empty, an empty stream is returned.
  225. - (instancetype)scanWithStart:(id)startingValue reduce:(id (^)(id running, id next))block;
  226. /// Combines each previous and current value into one object.
  227. ///
  228. /// This method is similar to -scanWithStart:reduce:, but only ever operates on
  229. /// the previous and current values (instead of the whole stream), and does not
  230. /// pass the return value of `reduceBlock` into the next invocation of it.
  231. ///
  232. /// start - The value passed into `reduceBlock` as `previous` for the
  233. /// first value.
  234. /// reduceBlock - The block that combines the previous value and the current
  235. /// value to create the reduced value. Cannot be nil.
  236. ///
  237. /// Examples
  238. ///
  239. /// RACSequence *numbers = @[ @1, @2, @3, @4 ].rac_sequence;
  240. ///
  241. /// // Contains 1, 3, 5, 7
  242. /// RACSequence *sums = [numbers combinePreviousWithStart:@0 reduce:^(NSNumber *previous, NSNumber *next) {
  243. /// return @(previous.integerValue + next.integerValue);
  244. /// }];
  245. ///
  246. /// Returns a new stream consisting of the return values from each application of
  247. /// `reduceBlock`.
  248. - (instancetype)combinePreviousWithStart:(id)start reduce:(id (^)(id previous, id current))reduceBlock;
  249. /// Takes values until the given block returns `YES`.
  250. ///
  251. /// Returns a stream of the initial values in the receiver that fail `predicate`.
  252. /// If `predicate` never returns `YES`, a stream equivalent to the receiver is
  253. /// returned.
  254. - (instancetype)takeUntilBlock:(BOOL (^)(id x))predicate;
  255. /// Takes values until the given block returns `NO`.
  256. ///
  257. /// Returns a stream of the initial values in the receiver that pass `predicate`.
  258. /// If `predicate` never returns `NO`, a stream equivalent to the receiver is
  259. /// returned.
  260. - (instancetype)takeWhileBlock:(BOOL (^)(id x))predicate;
  261. /// Skips values until the given block returns `YES`.
  262. ///
  263. /// Returns a stream containing the values of the receiver that follow any
  264. /// initial values failing `predicate`. If `predicate` never returns `YES`,
  265. /// an empty stream is returned.
  266. - (instancetype)skipUntilBlock:(BOOL (^)(id x))predicate;
  267. /// Skips values until the given block returns `NO`.
  268. ///
  269. /// Returns a stream containing the values of the receiver that follow any
  270. /// initial values passing `predicate`. If `predicate` never returns `NO`, an
  271. /// empty stream is returned.
  272. - (instancetype)skipWhileBlock:(BOOL (^)(id x))predicate;
  273. /// Returns a stream of values for which -isEqual: returns NO when compared to the
  274. /// previous value.
  275. - (instancetype)distinctUntilChanged;
  276. @end
  277. @interface RACStream (Deprecated)
  278. - (instancetype)sequenceMany:(RACStream * (^)(void))block __attribute__((deprecated("Use -flattenMap: instead")));
  279. - (instancetype)scanWithStart:(id)startingValue combine:(id (^)(id running, id next))block __attribute__((deprecated("Renamed to -scanWithStart:reduce:")));
  280. - (instancetype)mapPreviousWithStart:(id)start reduce:(id (^)(id previous, id current))combineBlock __attribute__((deprecated("Renamed to -combinePreviousWithStart:reduce:")));
  281. @end