Question

I'm creating a music player that will either respond to the user tapping a certain table row, or using the prev/next buttons on his remote.

To get a signal representing the index of the current playing track for the first case, I'd make it a function of the tapped row, eg:

RACSignal *didSelectS = [[self rac_signalForSelector:@selector(tableView:didSelectRowAtIndexPath:)] map:...

The latter case, I'd use a scanWithStart:reduce: where I'd pass in +1/-1 depending on which button the user presses and return a new absolute index according to this, eg.

RACSignal *prevS = [[remoteControlSignal filter:^BOOL(UIEvent *event) {
    return event.subtype == UIEventSubtypeRemoteControlPreviousTrack;
}] mapReplace:@-1];

RACSignal *nextS = [... mapReplace:@1];

RACSignal *trackIdxS = [RACSignal merge:@[nextS, prevS]] 
                                 scanWithStart:@0
                                 reduce:^id(NSNumber *running, NSNumber *next) {
                                   return @(running.integerValue + next.integerValue);
                                 }];

My problem is, how do I combine these two? One solution would be to wrap the values in an object, so that I in my scanWithStart:reduce: could distinguish between an absolute or a relative value, eg:

RACSignal *currentIndexSignal = [[RACSignal merge:@[didSelectS, prevS, nextS] scanWithStart:@0 reduce:^id(NSNumber *running, id next){
   if ([next isKindOfClass:[PrevNextValueWrapper class]]){
     // next is a wrapped relative value, eg -1/+1
     return @(running.integerValue + next.wrappedNumber.integerValue)
   } else {
     // next is an absolute value
     return next;
   }
}

But the isKindOfClass and the wrapped object just doesn't feel right ...

Was it helpful?

Solution

You could map to blocks instead of values,

NSNumber * (^nextTrack)(NSNumber *) = ^(NSNumber *currentTrack) {
    return @(currentTrack.integerValue + 1);
};

NSNumber * (^previousTrack)(NSNumber *) = ^(NSNumber *currentTrack) {
    return @(currentTrack.integerValue - 1);
};

Now -mapReplace: to those instead of @1/@-1.

For track selection, -map: (or here -reduceEach:) the signal to a block that ignores the track argument and returns the captured track:

reduceEach:^(id tableView, NSIndexPath *indexPath) {
    NSInteger newTrack = indexPath.row;
    return ^(NSNumber *currentTrack) {
        return @(newTrack);  
    };
}

Now, these can be merged and scanned:

RACSignal *currentIndexSignal = [[RACSignal
    merge:@[didSelectS, prevS, nextS]]
    scanWithStart:@0 reduce:^(NSNumber *running, NSNumber * (^trackChanger)(NSNumber *)) {
        return trackChanger(running);
    }];
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top