Search code examples
chainingreactive-cocoa

How to execute an array of operations in order with possible breaks in the middle with ReactiveCocoa


Suppose that I have a telephony application. I have a feature that I want to try calling an array of users one by one and break the sequence whenever one of the users accepts call, or when the complete operation is cancelled.

I will try to simplify it like this in pseudocode:

for(user in users) {
    result = callUserCommand(user);
    if(result == "accepted" || result == "cancelled") {
        break;
    }
}

Here, the callUserCommand is a RACCommand that needs to be async. And it can actually have three return values: "accepted", "cancelled", "declined".

Accepted and Cancelled will break the sequence of operations and won't execute the rest. Declined, should continue with the execution of the rest of the sequence.

I tried with something like the following, but really couldn't accomplish exactly the thing I described above.

    RACSignal *signal = [RACSignal concat:[users.rac_sequence map:^(User * user) {
        return [self.callUserCommand execute:user];
    }]];

    [signal subscribeNext:^(id x) {

    } error:^(NSError *error) {

    } completed:^{

    }];

Solution

  • If I understood correctly you would like to execute the sequence one by one until one of the call gets accepted or cancelled.

    Maybe you could give takeUntil or takeWhile a try. I would write this scenario with RAC like this:

    NSArray* users = @[@"decline", @"decline", @"decline", @"accept", @"decline"];
    
    [[[[[users.rac_sequence signal]
        flattenMap:^RACStream *(NSString* userAction) {
            NSLog(@"Calling user (who will %@):", userAction);
    
            // return async call signal here
            return [RACSignal return:userAction];
        }]
        takeWhileBlock:^BOOL(NSString* resultOfCall) {
            return [resultOfCall isEqualToString:@"decline"];
        }]
        doCompleted:^{
            NSLog(@"Terminated");
        }]
        subscribeNext:^(NSString* userAction) {
            NSLog(@"User action: %@", userAction);
        }];
    

    In the sample code above the last user who would decline the call won't be called.