0

I have a method that looks something like this:

- (RACSignal*)savedObjectsOfEntityType:(NSEntityDescription*)entity
{
    return [[[[NSNotificationCenter defaultCenter] rac_addObserverForName:NSManagedObjectContextDidSaveNotification object:nil] map:^id(NSNotification *notification) {
        NSMutableArray *allSaves = [NSMutableArray array];
        [allSaves addObjectsFromArray:notification.userInfo[NSInsertedObjectsKey]];
        [allSaves addObjectsFromArray:notification.userInfo[NSUpdatedObjectsKey]];
        [allSaves addObjectsFromArray:notification.userInfo[NSDeletedObjectsKey]];
        return allSaves;
    }] filter:^BOOL(NSArray *allSaves) {
        // Can filter only if I get individual objects
    }];
}

I am mapping the notification objects returned by an observer on the notification center. Each notification contains several array that I merge together in the allSaves array. On the next step I would need to filter the individual objects in that array. The problem is the object I get from the map is the array of objects and it doesn't make sense to filter on it. Instead I would need the map to send the objects in allSaves one by one. The map operator doesn't do this, but is there another operator that can take an array and pass out individual objects from it one by one?

Mihai Damian
  • 11,193
  • 11
  • 59
  • 81

1 Answers1

1
RACSignal *saves = [[NSNotificationCenter defaultCenter] rac_addObserverForName:NSManagedObjectContextDidSaveNotification object:nil]; // you should include the managed object context you want to watch here, but that's a separate issue
return [[[saves map:^(NSNotification *notification) {
    NSMutableArray *allSaves = [NSMutableArray array];
    [allSaves addObjectsFromArray:notification.userInfo[NSInsertedObjectsKey]];
    [allSaves addObjectsFromArray:notification.userInfo[NSUpdatedObjectsKey]];
    [allSaves addObjectsFromArray:notification.userInfo[NSDeletedObjectsKey]];
    return [[allSaves rac_sequence] signal];
}] switchToLatest] filter:^BOOL(NSManagedObject *savedObject) {

}];

We convert the array of objects, allSaves, into an RACSequence. Then we convert the RACSequence into an RACSignal and return that (there's no way to go directly from array to signal AFAIK). So we're mapping "signal of saves" into "signal of objets saved in a particular step".

switchToLatest takes that second signal and subscribes to it, such that the next filter, instead of receiving "signal of objects saved", receives each "object saved." switchToLatest turns a Signal<Signal<T>> into a Signal<T>, conceptually. So it's like we're unwrapping the inner signal.

There's another way we could do this, with flattenMap instead of map/switchToLatest. I believe in this case it behaves identically, since the inner signal is finite and will complete soon, so you could save yourself a few characters that way. But check that I'm right first.

Ian Henry
  • 22,255
  • 4
  • 50
  • 61
  • 2
    If `NSManagedObjectContextDidSaveNotification` occurs more than once, the use of `-switchToLatest` above would drop any values that hadn't been delivered yet (and switch to the new signal). I don't think that's a problem in this specific case, since the values will be processed synchronously. But to me, `-flattenMap:` seems more idiomatic for the case where you have an outer signal of values, and you want to map each value into an inner signal and then flatten that into the outer signal. It's not only clearer in intent, but is one fewer RACSignal operation, too. Just my two cents. – erikprice Jul 16 '14 at 20:19
  • Ok, I see how this works. Thanks! Found this relevant as well: https://github.com/ReactiveCocoa/ReactiveCocoa/issues/448 – Mihai Damian Jul 19 '14 at 19:57