B.J. Ray
B.J. Ray

Reputation: 23

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.

Upvotes: 2

Views: 216

Answers (2)

Chris Morse
Chris Morse

Reputation: 334

I am just learning ReactiveCocoa myself but I wanted to point out something important which also agrees with lightice11's answer. concat combines signals sequentially. Meaning you won't get anything from #2 or #3 until #1 completes. merge on the other hand interleaves the responses returning whatever comes next regardless of order. So for your scenario, it sounds like you really do want merge.

To quote the man, Justin Spahr-Summers:

concat joins the streams sequentially, merge joins the signals on an as-soon-as-values-arrive basis, switch only passes through the events from the latest signal.

Upvotes: 1

hhanesand
hhanesand

Reputation: 1000

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.

Upvotes: 1

Related Questions