Bubble Foundry


Subscribing to RestKit changes using ReactiveCocoa

by Peter.

Here at Lua we’re using both RestKit and ReactiveCocoa in our iOS app. Today we ran into a problem: we wanted to update a UI element in a view controller based upon changes to an object retrieved via RestKit. We had something like:

RAC(self.nameLabel, text) = RACObserve(user, name);

However, the UI never updated! Why?

Knowing that ReactiveCocoa uses KVO under the hood, I suspected that the update events were not being received. Colin and I investigated and learned that KVO events only exist on the thread they were generated on.

Was ReactiveCocoa observing the object on the wrong thread and so never receiving any KVO events? We tested this by just adding a simple subscribeNext: block:

[RACObserve(user, name) subscribeNext:^(id newName) {
  NSLog(@"new name: %@", newName);
}];

All the changes were logged!

Given this, the problem had to be on the receiving end. We read upon on RestKit and learned that it does its mapping operations on an NSOperationQueue. And yet, we were seeing the results in the subscribeNext: block.

Every good Objective-C developer knows they’re supposed to update views on the main queue. Perhaps this was the problem?

Looking at RACSignal, we noticed that it had a deliverOn: method. Sure enough, we could fix everything by explicitly telling the signal that we wanted to use its results on the main queue:

RAC(self.nameLabel, text) = [RACObserve(user, name) deliverOn:RACScheduler.mainThreadScheduler];

So what’s going on here? As best as I can tell, ReactiveCocoa is able to observe objects on all queues/threads, yet it delivers its results on the one on which it received the value. This is very logical, but you need to ensure that UI operations happen on the main queue or strange things will happen. Specifying the delivery scheduler ensures this.