Search code examples
iosobjective-creactive-cocoa

Two way binding with custom signal with Reactive Cocoa not working as expected


I have custom UITextField which has NSDecimalNumber value. I want to bind it two way to my model's price keypath.

Problem is that price is field is brutto price and is dependent on my custom build signal (sum of price, tax, discount etc signal)

So I tried to create RACChannelTerminal's like in code below but I am still getting infinite loop of updates. TextField is updating price, and price is updating TextField.

Anyone can give me a hint how to make this Two-Way data binding work?

// Connecting two way PRICE
RACChannel *channelForProductPrice = [[RACChannel alloc] init];
[channelForProductPrice.followingTerminal subscribeNext:^(NSDecimalNumber *newValue) {
    self.product.price = [Price priceWithDecimalNumber:newValue];
}];
[[[[self.product rac_signalForPrice] takeUntil:[channelForProductPrice.followingTerminal
                                                ignoreValues]] map:^NSDecimalNumber *(Price *nettoPrice) {
    return [self.product calculatePriceWithTax:YES withDiscount:NO unit:YES rounded:YES].value;
}] subscribe:channelForProductPrice.followingTerminal];


RACChannel *channelForBruttoTextField = [[RACChannel alloc] init];
[channelForBruttoTextField.followingTerminal subscribeNext:^(NSDecimalNumber *value) {
    self.bruttoPriceTextField.value = value;
}];
[[[[self.bruttoPriceTextField rac_signalForValueChange] takeUntil:[channelForBruttoTextField.followingTerminal ignoreValues]] map:^id(id value) {
    NSDecimalNumber *newPrice = [value decimalNumberByDividingBy:self.product.tax.taxRatePlusOneAsPercentage];
    return newPrice;
}] subscribe:channelForBruttoTextField.followingTerminal];


[channelForProductPrice.leadingTerminal subscribe:channelForBruttoTextField.leadingTerminal];
[channelForBruttoTextField.leadingTerminal subscribe:channelForProductPrice.leadingTerminal];

Solution

  • It looks like you're piping followingTerminal -(map)-> price -> followingTerminal, and there's nothing there to break that cycle. I don't think that is really the configuration you mean.

    You might be better off setting up some RACSignals rather than RACChannels, breaking cycles with distinctUntilChanged or similar, and with skip: as appropriate to stop bad values flowing when you hook things up. At the moment, the exact flow of information through this isn't clear to me.