I just started using ReactiveCocoa, so I'd appreciate any other tips for this.
If you only want one command or the other to execute at a time, one way to achieve this is to just use a single command (and disable concurrent execution, as you have already done):
1 const NSUInteger kLoad = 0;
2 const NSUInteger kReload = 1;
3
4 - (void)configureActiveSignal {
5 @weakify(self);
6 RACSignal *dba = [[self.didBecomeActiveSignal filter:^(id _) {
7 @strongify(self);
8 return self.data;
9 }]
10 mapReplace:@( kReload )];
11
12 RACSignal *ls = [self.command rac_liftSelector:@selector(execute:) withSignals:dba];
13 [[ls publish] connect];
14 }
15
16 - (void)configureCommand {
17 @weakify(self);
18 self.command = [[RACCommand alloc] initWithSignalBlock:^(NSNumber *loadOrReload) {
19 @strongify(self);
20 return RACTuplePack(loadOrReload, [self.rest dataSignalWithPage:self.page]);
21 }];
22
23 RAC(self, page) = [self.command.executionSignals reduceEach:^(NSNumber *loadOrReload, id _) {
24 @strongify(self);
25 return kReload == loadOrReload.integerValue ? @0 : @( ++self.page );
26 }];
27 RAC(self, data) = self.command.executionSignals reduceEach:^(NSNumber *loadOrReload, NSArray *data) {
28 @strongify(self);
29 if (kReload == loadOrReload.integerValue)
30 {
31 return [data mutableCopy];
32 }
33 else
34 {
35 NSMutableArray *ma = [self.data mutableCopy];
36 [ma addObjectsFromArray:data]
37 return ma;
38 }
39 }];
40
41 self.command.allowsConcurrentExecution = NO;
Nota bene, this is totally untested. But I hope it communicates the basic idea.
- You can infer from the code that there is only one command now, and its instance variable is named
command
. Instead of getting called with the array of data, it is now called with a constant to indicate whether it should load orreload, which you can see on line 18. - Line 5: Your code had a lot of retain cycles. You can't strongly reference
self
from within a block that is strongly referenced byself
(either directly, or via an indirect chain of object ownership). ReactiveCocoa comes with the@strongify/weakify
macros to help make this less arduous. As you can see, any time you need to referenceself
within a block that is owned by a command (which is itself owned byself
, thus the cycle), you need to create a weak reference to self and then use that inside the block. You use@strongify
inside of the block to avoid a data race with ARC's release mechanism. - Line 6: Transform the
didBecomeActive
notifications into executions ofself.command
with a parameter ofkReload
(this replaces your oldreloadCommand
), but only ifself.data != nil
, using-filter:
. - Line 12: Here's where you actually call
[self.command execute:]
, but you do this by lifting the selector onto the signal just created above. Presumably you have some other unshown code that used to executeself.loadCommand
, you will need to adjust that to pass@( kLoad )
instead. - Line 13: The
-publish/connect
idiom is conventionally used when you want to subscribe to a signal without actually doing anything with its value. This isn't strictly necessary in RAC 2.x. - Line 18: You can see that the command will call its signal block with a NSNumber now, that is, the constant that you have lifted up onto the signal created on line 6. However, where before the command returned just the data, it now returns a tuple containing both the input to
-execute:
and the data. This is because as you'll see below, downstream operations need access to both pieces of information. - Line 23: Instead of using the
RAC()
macro inside of a signal subscription block (which is almost always an antipattern), you now use it directly in the-configureCommand
method. Here we are saying that any time the command is executed, setself.page
to either0
or++self.page
, depending on whether the command was executed withkLoad
orkReload
. The-reduceEach:
operation is just a convenience to destructure the tuple being sent through the signal, and here we only care about the constant. Note the side-effect of incrementingself.page
in this-reduceEach:
operation. It's generally bad form to have side effects in your signal operations. The danger is that if something other thanRAC(self, page)
subscribes to the returned signal, the instance variable can be incremented more times than you would expect. It would be ideal if you could obtain the value ofself.page
from the source of data somehow, rather than tracking it in a global instance variable. - Line 27: This is just a variation on what you just did on line 23, but instead of setting
self.page
, you're settingself.data
. This time the-reduceEach:
operation does need to use the second value in the tuple (the data), which is transformed appropriately for eitherkLoad
orkReload
, and then ultimately set on the ivar.