Adding forward and reverse map operations to RACChannel #1069

Closed
olegam opened this Issue Jan 21, 2014 · 2 comments

Comments

Projects
None yet
2 participants
@olegam

olegam commented Jan 21, 2014

I'm still fairly new to the RACChannel thing, but it looks really good. One thing I would like to do to, but I don't know how is to chain in map operations.

For example I have a User class that implements NSCoding. I would like to automatically persist the currentUser property to NSUserDefaults using the NSUserDefaults (RACSupport) category.

If I just do something like

RACChannelTo(self, currentUser) = [defaults rac_channelTerminalForKey:kCurrentUserDefaultsKey];

then my object is never transformed to NSData so I get an exception. Hence I would like to have a forward mapping block that uses NSKeyedUnarchiver to map from NSData to a User object and have a backwards mapping block that maps my User object to NSData using an NSKeyedArchiver.

Is this possible somehow?

@Coneko

This comment has been minimized.

Show comment
Hide comment
@Coneko

Coneko Jan 21, 2014

Member

Terminals are signals and subscribers, so you can use any of the signal methods on them.

Unfortunately

RACChannelTo(self, currentUser) = [defaults rac_channelTerminalForKey:kCurrentUserDefaultsKey];

is pretty opaque, so, but if you look in RACKVOChannel.m the magic is demystified:

- (void)setObject:(RACChannelTerminal *)otherTerminal forKeyedSubscript:(NSString *)key {
    NSCParameterAssert(otherTerminal != nil);

    RACChannelTerminal *selfTerminal = [self objectForKeyedSubscript:key];
    [otherTerminal subscribe:selfTerminal];
    [[selfTerminal skip:1] subscribe:otherTerminal];
}

this means in your case:

RACChannelTerminal *currentUserTerminal = RACChannelTo(self, currentUser);
RACChannelTerminal *defaultsTerminal = [defaults rac_channelTerminalForKey:kCurrentUserDefaultsKey];

[defaultsTerminal subscribe:currentUserTerminal];
[[currentUserTerminal skip:1] subscribe:defaultsTerminal];

at which point you can map back and forth like this:

RACChannelTerminal *currentUserTerminal = RACChannelTo(self, currentUser);
RACChannelTerminal *defaultsTerminal = [defaults rac_channelTerminalForKey:kCurrentUserDefaultsKey];

[[defaultsTerminal map:^(id user){
    return [NSKeyedArchiver archivedDataWithRootObject:user];
} subscribe:currentUserTerminal];
[[[currentUserTerminal skip:1] map:^(NSData *data){
    return [NSKeyedUnarchiver unarchiveObjectWithData:data];
}] subscribe:defaultsTerminal];

Sorry for the longwinded explanation, give a man a fish and all that.

Member

Coneko commented Jan 21, 2014

Terminals are signals and subscribers, so you can use any of the signal methods on them.

Unfortunately

RACChannelTo(self, currentUser) = [defaults rac_channelTerminalForKey:kCurrentUserDefaultsKey];

is pretty opaque, so, but if you look in RACKVOChannel.m the magic is demystified:

- (void)setObject:(RACChannelTerminal *)otherTerminal forKeyedSubscript:(NSString *)key {
    NSCParameterAssert(otherTerminal != nil);

    RACChannelTerminal *selfTerminal = [self objectForKeyedSubscript:key];
    [otherTerminal subscribe:selfTerminal];
    [[selfTerminal skip:1] subscribe:otherTerminal];
}

this means in your case:

RACChannelTerminal *currentUserTerminal = RACChannelTo(self, currentUser);
RACChannelTerminal *defaultsTerminal = [defaults rac_channelTerminalForKey:kCurrentUserDefaultsKey];

[defaultsTerminal subscribe:currentUserTerminal];
[[currentUserTerminal skip:1] subscribe:defaultsTerminal];

at which point you can map back and forth like this:

RACChannelTerminal *currentUserTerminal = RACChannelTo(self, currentUser);
RACChannelTerminal *defaultsTerminal = [defaults rac_channelTerminalForKey:kCurrentUserDefaultsKey];

[[defaultsTerminal map:^(id user){
    return [NSKeyedArchiver archivedDataWithRootObject:user];
} subscribe:currentUserTerminal];
[[[currentUserTerminal skip:1] map:^(NSData *data){
    return [NSKeyedUnarchiver unarchiveObjectWithData:data];
}] subscribe:defaultsTerminal];

Sorry for the longwinded explanation, give a man a fish and all that.

@olegam

This comment has been minimized.

Show comment
Hide comment
@olegam

olegam Jan 21, 2014

That's awesome! Thanks you so much. I had to make a few minor changes to get it right (a missing bracket and swapped archiver and unarchiver). This works for me:

NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
RACChannelTerminal *currentUserTerminal = RACChannelTo(self, currentUser);
RACChannelTerminal *defaultsTerminal = [defaults rac_channelTerminalForKey:kCurrentUserDefaultsKey];

[[[defaultsTerminal ignore:nil] map:^(NSData *data) {
    return [NSKeyedUnarchiver unarchiveObjectWithData:data];
}] subscribe:currentUserTerminal];

[[[currentUserTerminal skip:1] map:^(FIBUser *user){
    return [NSKeyedArchiver archivedDataWithRootObject:user];
}] subscribe:defaultsTerminal];

olegam commented Jan 21, 2014

That's awesome! Thanks you so much. I had to make a few minor changes to get it right (a missing bracket and swapped archiver and unarchiver). This works for me:

NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
RACChannelTerminal *currentUserTerminal = RACChannelTo(self, currentUser);
RACChannelTerminal *defaultsTerminal = [defaults rac_channelTerminalForKey:kCurrentUserDefaultsKey];

[[[defaultsTerminal ignore:nil] map:^(NSData *data) {
    return [NSKeyedUnarchiver unarchiveObjectWithData:data];
}] subscribe:currentUserTerminal];

[[[currentUserTerminal skip:1] map:^(FIBUser *user){
    return [NSKeyedArchiver archivedDataWithRootObject:user];
}] subscribe:defaultsTerminal];
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment