逆引きReactiveCocoa: シグナルの現在の値だけでなく、直前の値にアクセスする

元ネタ: How to obtain KVO old value using ReactiveCocoa 2.0 API? · Issue #762 · ReactiveCocoa/ReactiveCocoa


KVOのオプションを使用する場合

ReactiveCocoaではRACObserve()マクロによってKVOを非常に簡単に扱うことができますが、それだけでは、通常のKVOで使用するaddObserver:forKeyPath:options:context:の場合には指定できるNSKeyValueObservingOptions (NSKeyValueObservingOptionOldなど) を指定できません。

KVOのオプション指定を行いたい場合、-[NSObject rac_valuesAndChangesForKeyPath:options:observer:]を使用するとKVOのオプション指定を反映したシグナルを得ることができます。ただし、この戻り値のシグナルはRACObserve()とは違い、nextとして、変更後の値とチェンジディクショナリーからなるRACTupleを送るため、-reduceEach:を使用するなどしてタプルの値を分解・チェンジディクショナリーから変更前の値を取得するなどの必要があります。

[[self 
    rac_valuesAndChangesForKeyPath:keyPath options:NSKeyValueObservingOptionOld observer:nil] 
    reduceEach:^(id value, NSDictionary *changeDictionary) {
        id oldValue = changeDictionary[NSKeyValueChangeOldKey];
        return // 何かしらの値;
    }];

オペレーターを使用する場合

-[RACSignal combinePreviousWithStart:reduce:]を使用すると、reduce:のブロック引数で以下のように直前の値を使用することができます。

RACSignal *signal = [@[ @1, @2, @3, @4 ].rac_sequence signal];
[[signal 
    // 最初の`next`では`previous`に1つ目の引数(この場合は`@0`)が渡され、
    // 2回目以降の`next` では前回の`current`が渡される。
    combinePreviousWithStart:@0 reduce:^(NSNumber *previous, NSNumber *current) {
        return @(previous.integerValue + current.integerValue);
    }]
    subscribeNext:^(NSNumber *value) {
        NSLog(@"%d", value.integerValue);
    }];
// 1 (<= 0 + 1)
// 3 (<= 1 + 2)
// 5 (<= 2 + 3)
// 7 (<= 3 + 4)