RACKVOChannel.h 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  1. //
  2. // RACKVOChannel.h
  3. // ReactiveCocoa
  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 "EXTKeyPathCoding.h"
  10. #import "metamacros.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. /// A RACChannel that observes a KVO-compliant key path for changes.
  60. @interface RACKVOChannel : RACChannel
  61. /// Initializes a channel that will observe the given object and key path.
  62. ///
  63. /// The current value of the key path, and future KVO notifications for the given
  64. /// key path, will be sent to subscribers of the channel's `followingTerminal`.
  65. /// Values sent to the `followingTerminal` will be set at the given key path using
  66. /// key-value coding.
  67. ///
  68. /// When the target object deallocates, the channel will complete. Signal errors
  69. /// are considered undefined behavior.
  70. ///
  71. /// This is the designated initializer for this class.
  72. ///
  73. /// target - The object to bind to.
  74. /// keyPath - The key path to observe and set the value of.
  75. /// nilValue - The value to set at the key path whenever a `nil` value is
  76. /// received. This may be nil when connecting to object properties, but
  77. /// an NSValue should be used for primitive properties, to avoid an
  78. /// exception if `nil` is received (which might occur if an intermediate
  79. /// object is set to `nil`).
  80. #if OS_OBJECT_HAVE_OBJC_SUPPORT
  81. - (id)initWithTarget:(__weak NSObject *)target keyPath:(NSString *)keyPath nilValue:(id)nilValue;
  82. #else
  83. // Swift builds with OS_OBJECT_HAVE_OBJC_SUPPORT=0 for Playgrounds and LLDB :(
  84. - (id)initWithTarget:(NSObject *)target keyPath:(NSString *)keyPath nilValue:(id)nilValue;
  85. #endif
  86. - (id)init __attribute__((unavailable("Use -initWithTarget:keyPath:nilValue: instead")));
  87. @end
  88. /// Methods needed for the convenience macro. Do not call explicitly.
  89. @interface RACKVOChannel (RACChannelTo)
  90. - (RACChannelTerminal *)objectForKeyedSubscript:(NSString *)key;
  91. - (void)setObject:(RACChannelTerminal *)otherTerminal forKeyedSubscript:(NSString *)key;
  92. @end