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