Search code examples
iosobjective-creactive-cocoa

How to know when all objects are saved asynchronously using ReactiveCocoa


In my app, I am using ReactiveCocoa to return signals to notify me when async api calls are completed (successfully or not). On the POST for saving the data, it only takes one object at a time:

- (RACSignal *)postJSONData:(NSDictionary *)dict toRelativeURL:(NSString *)urlString;.

The function that returns a RACSignal sends the subscriber a next:

[subscriber sendNext:json]or an Error: [subscriber sendError:jsonError].

This works great when saving a single object but I also have a scenario where I have to save multiple objects. These objects can be saved in any order (i.e. they are not dependent on one another) or sequentially - it doesn't matter to me.

I need to update the UI indicating the overall progress (Saving 1 of 4, Saving 2 of 4....) as well as a final progress update (Completed 4 of 4) and specific action to take when all have been processed (successful or not).

There are a number of ways to do this, but I'd like to do this the proper way using ReactiveCocoa. I'm thinking I either can do this with a concat: or then: with a rac_sequence map:^, but I'm not sure. On their github page, they show an example of addressing parallel work streams, but they use 2 discretely defined signals. I won't have my signals until I loop through each object I need to save. Would love some guidance or an example (even better!). Thanks in advance.


Solution

  • I'm doing something similar in my app where I start 3 different async network calls and combine them all into one signal that I can listen to. Basically I loop through all my objects and store the network signal in an array. I then call merge: and pass it the array of network signals.

    NSMutableArray *recievedNames = [NSMutableArray new];
    NSMutableArray *signals = [NSMutableArray new];
    
    //go though each database that has been added and grab a signal for the network request
    for (GLBarcodeDatabase *database in self.databases) {
        [signals addObject:[[[[self.manager rac_GET:[database getURLForDatabaseWithBarcode:barcode] parameters:nil] map:^id(RACTuple *value) {
            return [((NSDictionary *)value.second) valueForKeyPath:database.path];
        }] doError:^(NSError *error) {
            NSLog(@"Error while fetching name from database %@", error);
        }]
    }
    
    //forward all network signals into one signal
    return [[[RACSignal merge:signals] doNext:^(NSString *x) {
        [recievedNames addObject:x];
    }] then:^RACSignal *{
        return [RACSignal return:[self optimalNameForBarcodeProductWithNameCollection:recievedNames]];
    }];
    

    Feel free to ask me questions about any of the operators I have used and I will do my best to explain them.