0

I am trying to implement a RACCommand that can be executed to initiate display of the freshest available data to the UI.

Both sendNext: calls would return data from the local database and a remote server respectively.

Is there are more concise way to achieve the same behaviour?

self.testObjects = @[ @"initial" ];

RACCommand *command = [[RACCommand alloc] initWithSignalBlock:^RACSignal *(id input) {
    RACSignal *s1 = [RACSignal createSignal:^RACDisposable *(id <RACSubscriber> subscriber) {
        [subscriber sendNext:@[@"value1", @"value2"]];
        [subscriber sendCompleted];
        return [RACDisposable disposableWithBlock:^{
            // Cleanup
        }];
    }];

    RACSignal *s2 = [RACSignal createSignal:^RACDisposable *(id <RACSubscriber> subscriber) {
        [subscriber sendNext:@[ @"value1-updated", @"value2" ]];
        [subscriber sendCompleted];
        return [RACDisposable disposableWithBlock:^{
            // Cleanup
        }];
    }];

    return [RACSignal createSignal:^RACDisposable *(id <RACSubscriber> subscriber) {
        [s1 subscribeNext:^(id x) {
            [subscriber sendNext:x];
        }];

        [s1 subscribeCompleted:^{
            [s2 subscribeNext:^(id x) {
                [subscriber sendNext:x];
            }];
        }];

        return [RACDisposable disposableWithBlock:^{
            // Cleanup
        }];
    }];
}];

RACSignal *racSignal = [command.executionSignals switchToLatest];
RAC(self, testObjects) = racSignal;

[[self rac_valuesAndChangesForKeyPath:@"testObjects" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionInitial observer:self] subscribeNext:^(RACTuple *x) {
    RACTupleUnpack(NSArray *values ) = x;
    NSLog(@"values: %@", values);
}];

[command execute:nil];

Outputs:

values: ( initial )

values: ( value1, value2 )

values: ( "value1-updated", value2 )

nacross
  • 2,013
  • 2
  • 25
  • 37

1 Answers1

1

Assuming you can do all of your cleanup in the disposables for each of s1 and s2 (that is, assuming you don't have any global cleanup that you're not mentioning), you can use the -[RACSignal concat:] method to chain these two signals together:

RACCommand *command = [[RACCommand alloc] initWithSignalBlock:^RACSignal *(id input) {
    RACSignal *s1 = [RACSignal createSignal:^RACDisposable *(id <RACSubscriber> subscriber) {
        [subscriber sendNext:@[@"value1", @"value2"]];
        [subscriber sendCompleted];
        return [RACDisposable disposableWithBlock:^{
            // Cleanup
        }];
    }];

    RACSignal *s2 = [RACSignal createSignal:^RACDisposable *(id <RACSubscriber> subscriber) {
        [subscriber sendNext:@[ @"value1-updated", @"value2" ]];
        [subscriber sendCompleted];
        return [RACDisposable disposableWithBlock:^{
            // Cleanup
        }];
    }];

    return [s1 concat:s2];
}];

The -[RACSignal concat:] method in this case will create a signal which delivers all values on s1, and then when s1 completes will deliver values on s2, until it completes (at which time the returned signal will complete).

erikprice
  • 6,240
  • 3
  • 30
  • 40
  • Thanks erik, this does work and also revealed my own confusion about how the RACCommand's executionSignals works with switchToLatest. I had expected that as the concat signal completed testObjects would be updated only once but this doesn't happen, I assume because executionSignals doesn't complete it just keeps sending the next signal. – nacross Mar 06 '14 at 23:46
  • 1
    The block passed to `-[RACCommand initWithSignalBlock:]` will get called every time the command is executed. So technically, the concat'd signal *does* only update `testObjects` once, but a new concat'd signal is created every time the command is executed, so the net effect is that `testObjects` gets updated multiple times. – erikprice Mar 08 '14 at 20:08