Search code examples
objective-creactive-cocoa

Add and remove from array carried by RACSignal


I have one RACSignal that carries NSArray of objects of class Bookmark

RACSignal *bookmarksSignal = ... // Carries @[[[Bookmark alloc] init], ...]

And two RACCommands to add and delete bookmark

RACCommand *addBookmarkCommand = [[RACCommand alloc] initWithSignalBlock:^RACSignal *(id input){
        return [RACSignal return:input];
    }];
...
[addBookmarkCommand execute: bookmark1];
RACCommand *deleteBookmarkCommand = [[RACCommand alloc] initWithSignalBlock:^RACSignal *(id input){
        return [RACSignal return:input];
    }];
...
[deleteBookmarkCommand execute: bookmark2]

Commands are executed on user interaction. How can I combine bookmarksSignal and executions signals of both commands to create signal that carries NSArray of bookmarks that contains original bookmarks with those added by addBookmarkCommand and without those removed by deleteBookmarkCommand?

(bookmarks are compared by url property)

I'm afraid that I'm missing something obvious, but I can't figure out how to do this in pure way.


Solution

  • For lack of time, I'll go with a more laconic answer compared to @IanHenry's :)

    For anything here that doesn't make sense, please comment and I'll be happy explain in detail.

    // Map additions into blocks that add the bookmark to the given mutable array.
    RACSignal *addBookmarkSignal = [[addBookmarkCommand concat] map:^(Bookmark *bookmark) {
        return ^(NSMutableArray *bookmarks) {
            return [bookmarks addObject:bookmark];
        };
    }];
    
    // Map deletions into blocks that remove the bookmark from the given mutable array.
    RACSignal * addBookmarkSignal = [[deleteBookmarkCommand concat] map:^(Bookmark *bookmark) {
        return ^(NSMutableArray *bookmarks) {
            [bookmarks removeObject:bookmark];
        };
    }];
    
    // Combine the add and delete functions into a single signal.
    RACSignal *updatesSignal = [RACSignal merge:@[addBookmarkSignal, addBookmarkSignal]];
    
    RACSignal *updatedBookmarksSignal = [[bookmarksSignal
        // Each time bookmarksSignal sends an array, this -map: builds a
        // signal that updates the latest list of bookmarks, and sends it. 
        map:^(NSArray *bookmarks) {
            NSMutableArray *mutableBookmarks = [bookmarks mutableCopy];
    
            // Using the update blocks from the add/delete commands,
            // produce the modified list of bookmarks.
            return [[updatesSignal
                map:^(void (^update)(NSMutableArray *)) {
                    update(mutableBookmarks);
                    return [mutableBookmarks copy];
                }]
                startWith:bookmarks];
        }]
        // When bookmarksSignal sends anew, switch to the newest signal of updates.
        switchToLatest];