RACKVOChannel.h 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  1. //
  2. // RACKVOChannel.h
  3. // ReactiveObjC
  4. //
  5. // Created by Uri Baghin on 27/12/2012.
  6. // Copyright (c) 2012 GitHub, Inc. All rights reserved.
  7. //
  8. #import "RACChannel.h"
  9. #import <ReactiveObjC/RACEXTKeyPathCoding.h>
  10. #import "RACmetamacros.h"
  11. /// Creates a RACKVOChannel to the given key path. When the targeted object
  12. /// deallocates, the channel will complete.
  13. ///
  14. /// If RACChannelTo() is used as an expression, it returns a RACChannelTerminal that
  15. /// can be used to watch the specified property for changes, and set new values
  16. /// for it. The terminal will start with the property's current value upon
  17. /// subscription.
  18. ///
  19. /// If RACChannelTo() is used on the left-hand side of an assignment, there must a
  20. /// RACChannelTerminal on the right-hand side of the assignment. The two will be
  21. /// subscribed to one another: the property's value is immediately set to the
  22. /// value of the channel terminal on the right-hand side, and subsequent changes
  23. /// to either terminal will be reflected on the other.
  24. ///
  25. /// There are two different versions of this macro:
  26. ///
  27. /// - RACChannelTo(TARGET, KEYPATH, NILVALUE) will create a channel to the `KEYPATH`
  28. /// of `TARGET`. If the terminal is ever sent a `nil` value, the property will
  29. /// be set to `NILVALUE` instead. `NILVALUE` may itself be `nil` for object
  30. /// properties, but an NSValue should be used for primitive properties, to
  31. /// avoid an exception if `nil` is sent (which might occur if an intermediate
  32. /// object is set to `nil`).
  33. /// - RACChannelTo(TARGET, KEYPATH) is the same as the above, but `NILVALUE` defaults to
  34. /// `nil`.
  35. ///
  36. /// Examples
  37. ///
  38. /// RACChannelTerminal *integerChannel = RACChannelTo(self, integerProperty, @42);
  39. ///
  40. /// // Sets self.integerProperty to 5.
  41. /// [integerChannel sendNext:@5];
  42. ///
  43. /// // Logs the current value of self.integerProperty, and all future changes.
  44. /// [integerChannel subscribeNext:^(id value) {
  45. /// NSLog(@"value: %@", value);
  46. /// }];
  47. ///
  48. /// // Binds properties to each other, taking the initial value from the right
  49. /// side.
  50. /// RACChannelTo(view, objectProperty) = RACChannelTo(model, objectProperty);
  51. /// RACChannelTo(view, integerProperty, @2) = RACChannelTo(model, integerProperty, @10);
  52. #define RACChannelTo(TARGET, ...) \
  53. metamacro_if_eq(1, metamacro_argcount(__VA_ARGS__)) \
  54. (RACChannelTo_(TARGET, __VA_ARGS__, nil)) \
  55. (RACChannelTo_(TARGET, __VA_ARGS__))
  56. /// Do not use this directly. Use the RACChannelTo macro above.
  57. #define RACChannelTo_(TARGET, KEYPATH, NILVALUE) \
  58. [[RACKVOChannel alloc] initWithTarget:(TARGET) keyPath:@keypath(TARGET, KEYPATH) nilValue:(NILVALUE)][@keypath(RACKVOChannel.new, followingTerminal)]
  59. NS_ASSUME_NONNULL_BEGIN
  60. /// A RACChannel that observes a KVO-compliant key path for changes.
  61. @interface RACKVOChannel<ValueType> : RACChannel<ValueType>
  62. /// Initializes a channel that will observe the given object and key path.
  63. ///
  64. /// The current value of the key path, and future KVO notifications for the given
  65. /// key path, will be sent to subscribers of the channel's `followingTerminal`.
  66. /// Values sent to the `followingTerminal` will be set at the given key path using
  67. /// key-value coding.
  68. ///
  69. /// When the target object deallocates, the channel will complete. Signal errors
  70. /// are considered undefined behavior.
  71. ///
  72. /// This is the designated initializer for this class.
  73. ///
  74. /// target - The object to bind to.
  75. /// keyPath - The key path to observe and set the value of.
  76. /// nilValue - The value to set at the key path whenever a `nil` value is
  77. /// received. This may be nil when connecting to object properties, but
  78. /// an NSValue should be used for primitive properties, to avoid an
  79. /// exception if `nil` is received (which might occur if an intermediate
  80. /// object is set to `nil`).
  81. #if OS_OBJECT_HAVE_OBJC_SUPPORT
  82. - (instancetype)initWithTarget:(__weak NSObject *)target keyPath:(NSString *)keyPath nilValue:(nullable ValueType)nilValue;
  83. #else
  84. // Swift builds with OS_OBJECT_HAVE_OBJC_SUPPORT=0 for Playgrounds and LLDB :(
  85. - (instancetype)initWithTarget:(NSObject *)target keyPath:(NSString *)keyPath nilValue:(nullable ValueType)nilValue;
  86. #endif
  87. - (instancetype)init __attribute__((unavailable("Use -initWithTarget:keyPath:nilValue: instead")));
  88. @end
  89. /// Methods needed for the convenience macro. Do not call explicitly.
  90. @interface RACKVOChannel (RACChannelTo)
  91. - (RACChannelTerminal *)objectForKeyedSubscript:(NSString *)key;
  92. - (void)setObject:(RACChannelTerminal *)otherTerminal forKeyedSubscript:(NSString *)key;
  93. @end
  94. NS_ASSUME_NONNULL_END