Search code examples
objective-ccocoa-touchreactive-cocoa

Better way to observe in ReactiveCocoa


I'm still new to ReactiveCocoa. I wanted to simply add an observer to a field, so did it like this:

[_countryPicker rac_observeKeyPath:@"value" options:nil observer:self block:^(VBCountry* value, NSDictionary* change)
{
    if ([_mobileField.textField.text length] == 0)
    {
        [_mobileField.textField setText:[NSString stringWithFormat:@"+%i", value.dialCode]];
    }
}];

With block callback, and no need to explicitly detach the observer, this is already better than old-style KVO.

However, is this a low-level method to which there's a higher level of abstraction? If so, is it OK to call this method directly? And what's the better/higher way to do it?


Solution

  • I'd advise against depending on the direct KVO methods. They're really an implementation detail.

    Let's progressively re-write that with idiomatic RAC operators.

    First we'll just replace the direct KVO code with RACObserve:

    [RACObserve(_countryPicker, value) subscribeNext:^(VBCountry *value) {
        if ([_mobileField.textField.text length] == 0)
        {
            [_mobileField.textField setText:[NSString stringWithFormat:@"+%i", value.dialCode]];
        }
    }];
    

    Then we'll replace the if and string formatting with -filter: and -map::

    [[[RACObserve(_countryPicker, value) filter:^(id _) {
        return [_mobileField.textField.text length] > 0;
    }] map:^(VBCountry *value) {
        return [NSString stringWithFormat:@"+%i", value.dialCode];
    }] subscribeNext:^(NSString *text) {
        [_mobileField.textField setText:text];
    }];
    

    Finally we'll use the RAC macro to make the assignment over time explicit:

    RAC(_mobileField.textField, text) = [[RACObserve(_countryPicker, value) filter:^(id _) {
        return [_mobileField.textField.text length] > 0;
    }] map:^(VBCountry *value) {
        return [NSString stringWithFormat:@"+%i", value.dialCode];
    }];