How I Learned to Write Custom Signals in Reactive Cocoa

How I Learned to Write Custom Signals in Reactive Cocoa

Honestly, I started using Reactive Cocoa because it’s fashionable. I hear developers talking about this framework all the time and I can barely remember an iOS meetup without Reactive Cocoa being mentioned.

Reactive Cocoa

When I only started learning Reactive Cocoa I didn’t really know what it was. “Reactive” sounds really cool and “functional” sounds smart. But after I succumbed to the temptation of getting a hold of Reactive Cocoa I can’t imagine my coding hours without it.

Reactive Cocoa is just a framework (but a great one) which opens a window to the world of the functional reactive programming. It allows you to benefit from practical application of this paradigm without even requiring a deep theoretical knowledge of the FRP.

I learned Reactive Cocoa on a real project from doing some simple things first to solving two issues which I’ll tell you about in this article. I will talk about “what to do” rather than “how to do” so that you could get some practical understanding of the framework.

1. Bindings

Introduction to Reactive Cocoa usually starts with bindings. After all, they are the simplest thing to learn for a beginner.

The bindings by themselves are just an add-on to the existing KVO mechanism in the Objective-C. Is there anything new that Reactive Cocoa brings to KVO? A more user-friendly interface, for sure, adding to that an ability to describe the rules of binding the state of the model to the state of the UI in the declarative style.

Let’s look at the bindings on the example of a cell.

Usually a cell links to the model and displays its visual state (or the state of the view model for the MVVM adepts). Although, Reactive Cocoa is often considered in one context with MMVM and vice versa, it doesn’t really matter. The bindings are just a way to make your life easier right now.

- (void)awakeFromNib {
 [super awakeFromNib];
 RAC(self, titleLabel.text) = RACObserve(self, model.title);

This is the declarative style. "I want the text on my label to always equal the title of my model" --  read in the -awakeFromNib method. It doesn’t really matter when the title or the model changes.

When we looked at how it works under the hood, we discovered that RACObserve is a macro that takes your keypath (“mode.title” from object self in our case) and converts it to a RACSignal. The RACSignal is an object from Reactive Cocoa framework that represents and delivers the future data. In our example, it will deliver the data from “model.title” every time the model or the title changes.

We’ll talk about the signals a bit later, since there is no need to go into detail at this stage. For now, you can simply attach a state of the model to the interface and enjoy the results.

Quite often you’ll have to transform the state of the model for displaying it in interface. In this case you can use an operator -map:

RAC(self, titleLable.text) = [RACObserve(self, model.title) map:^id(NSString *text) {
 return [NSString stringWithFormat:@”title: %@”, text];


All the UI operations must be performed on the main thread. But, for example, the field title can be changed on the background thread (i.e. for data parsing). Here's what you need to add to ensure that a new value of the title will be delivered to the subscriber on the main thread:


RAC(self, titleLabel.text) = [RACObserve(self, model.title) deliverOnMainThread];

RACObserve is a macro expanded to -rac_valuesForKeyPath:observer: But here is a trick -- this macro always captures self as an observer. If you use RACObserve inside a block, you should make sure you're not causing a retain cycle and use a weak reference if needed. Reactive Cocoa has convenience @weakify and @strongify macros for this needs.

One more thing to notice about bindings is a case when your model state is binded to some significant UI changes, and model state changes frequently. This may negatively affect the app performance and to avoid this you can use operator -throttle: - it takes an NSTimeInterval and only sends nexts to the subscriber after the given time interval.

2. Operations on collections (filter, map, reduce)

Operations on collections were the next thing we explored in Reactive Cocoa. Working with arrays takes a lot of time, doesn’t it? While your application is running, the arrays of data are coming to you from a network (input) and require modifications, so that you can present it to a user in the required form (output).

The raw network data needs to be transformed into yours Models and View Models, and filtered for a user.

In Reactive Cocoa, collections are represented as a RACSequence class. There are categories for all types of collections in Cocoa that transform a Cocoa collection to a Reactive Cocoa collection. After these transformations, you’ll get a few functional methods like map, filter, and reduce.

Here is a short example from our project:

 RACSequence *sequence = [[[matchesViewModels rac_sequence] filter:^BOOL(MatchViewModel *match) {
 return [match hasMessages];
 }] map:^id(MatchViewModel *match) {
 return match.chatViewModel;

Firstly, we filter our view models to select those of them which already have messages (- (BOOL)hasMessages). Then we should transform them into other view models.

After you’re done with your sequence, it could be transformed back to the NSArray:

NSArray *chatsViewModels = [sequence array];

Have you noticed that we are using an operator -map: again? This time, though, it applies to RACSequence, and not to RACSignal, as it was with the bindings.

The great thing in the RAC architecture is that it has just two main classes - RACSignal and - RACSequence, which have a single parent - RACStream. Everything is a stream, but a signal is a push driven stream (new values are pushed to the subscribers and can’t be pulled), whereas a sequence is a pull driven stream (provides values whenever someone asks for them).

One more thing to notice is how we chained the operations together. It’s the key concept in the RAC, which is also applied to both the RACSignal and the RACSequence.

Read also: How to work with CloudKit

3. Networking

The next step in understanding the features of the framework is using it for networking. When I talked about bindings, I mentioned that RACObserve marco created a RACSignal which represents the data that will be delivered in the future. This object is perfect for representing a network request.

Signals send three types of events:

  • next - the future value/values;
  • error - an NSError* value that indicates that the signal can’t be completed successfully;
  • completed - indicates that the signal has been completed successfully.

The lifetime of a signal consists of any number of next events, followed by one error or completed event (but not both).

This is quite similar to how we used to write our network requests using blocks. But what’s the difference? Why to replace usual blocks with signals? Here are some reasons:

1) You get rid of the callback hell!

This nightmare in code happens when you have a few nested requests and each subsequent one uses the result of the previous one.

2) You propagate error in one place.

Here is a short example:

Suppose you have two signals -- loginUser and fetchUserInfo. Let’s create a signal that logs in a user and then fetches his info:

RACSignal *signal = [[networkClient loginUser] flattenMap:^RACStream *(User *user) {
 return [networkClient fetchUserInfo:user];

flattenMap block will be called when loginUser signal sends the next value and this value gets passed to the block via the user parameter". In flattenMap block we  take this value from the previous signal and produce a new signal as a result. Now, let’s subscribe to this signal:

 [signal subscribeError:^(NSError *error) {
 // error from one of the signals 
 } completed:^{
 // side effects goes here. block get called when both signals completed

It’s worth mentioning that subscribeError block will be called if at least one of the signals fails. If the first signal completes with an error, the second one will not be executed.

3) Signal has a built-in disposal (cancelling) mechanism.

For example, quite often a user leaves a screen during its loading. In this case, the loading operation needs to be canceled. You can implement this logic directly in the signal, instead of storing a link to the loading operation. For example, our signal for loading user data can be created in the following way:

[[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
 __block NSURLSessionDataTask *task = [self GET:url parameters:parameters completion:^(id response, NSError *error) {
 if (!error) {
 [subscriber sendNext:response];
 [subscriber sendCompleted];
 } else {
 [subscriber sendError:error];

 return [RACDisposable disposableWithBlock:^{
 [task cancel];


I simplified this code intentionally to show the idea, but in real life you probably shouldn’t capture self in subscribe block.

With a reference to the signal we can define when it should be disposed:

[[networkClient loadChats] takeUntil:self.rac_willDeallocSignal];


[[networkClient loadChats] takeUntil:[self.cancelButton rac_signalForControlEvents:UIControlEventTouchUpInside]];

The great thing about this approach is that you don’t have to store a link to the operation which you will only use when you need to complete it. Therefore сode looks more declarative as there is no need to save intermediate state.

Of course you also can cancel the signal manually -  just store reference to RACDisposable* object (which is returned from subsribeNext/Error/Completed method) and call -dispose method directly when you want.

The implementation of the network client using signals is a pretty broad topic to discuss. You can look at OctoKit - a great example of how to use Reactive Cocoa to handle networking issues. Ash Furrow’s also covered this topic in his book Functional Reactive Programming on iOS.

Read also: 8 tips on CoreData migration

4. Signals in action

When solving some programming tasks, we used to combine pieces of data and events from different parts of the application. This data appears and modifies asynchronously. If we think about it imperatively, we try to foresee what additional connections and variables must come up in code and more importantly, how to synchronize all that in time.

When we have formulated an approximate chain of actions that need to be completed, we start writing code, and different parts of a class or even several classes get fouled with new lines of code, if statements, useless states, which wander around our project as a Gypsy caravan.

You know how difficult it is to sort out such code! And sometimes the only way to figure out what’s happening is step by step debugging.

After some time of working with Reactive Cocoa, it came to me that the basis for solving all the above mentioned tasks (binding, operations on collections, networking) is representing an app’s lifecycle as a stream of data (RACStream). Then the data coming from a user or from a network should be transformed in a certain way. It turns out you can solve given tasks much easier this way!

I’ll give you two examples.

Task #1

This example comes from a real project that we recently finished.

We had a messaging feature in the app and one of the tasks was to display a proper badge count on the app’s icon. Usual stuff, isn’t it?

We had ChatViewModel class with a public unread boolean state.

@interface ChatViewModel : NSObject

@property(nonatomic, readonly) BOOL unread

// other public declarations


And somewhere in the code, we had an dataSource array that contained these view models.

What do we want to do here? We want to update the number of badges on the icon each time a model’s unread field changes. The badge count must equal the number of YES values in all models. Let’s make this sort of a signal:

RACSignal *unreadStatusChanged = [[RACObserve(self, dataSource) map:^id(NSArray *models) {
 RACSequence *sequence = [[models rac_sequence] map:^id(ChatViewModel *model) {
 return RACObserve(model, unread);

 return [[RACSignal combineLatest:sequence] map:^id(RACTuple *unreadStatuses) {
 return [unreadStatuses.rac_sequence foldLeftWithStart:@0 reduce:^id(NSNumber *accumulator, NSNumber *unreadStatus) {
 return @(accumulator.integerValue + unreadStatus.integerValue);
}] switchToLatest];

It may look a bit difficult for beginners, but it is not so hard to understand.

At first, we observe our array for changes:

RACObserve(self, dataSource)

It's important to us, because the logic assumes that new chats can be created, and an old chats can be deleted. Since RAC doesn’t have KVO for collection mutations, dataSource is set to an immutable array every time an object is added/removed to/from the dataSource. RACObserve will return a signal that will send a new array every time the dataSource is set to a new value.

Okay, we got a signal... But it isn’t the signal that we wanted, so we should transform it. Operator -map: works perfectly well for this case.

[RACObserve(self, dataSource) map:^id(NSArray *models) {


We got array of models in the block map. Since we want to know about each change of the unread property of each model, it seems we still need a signal, or even an array of signals -- one signal for each model:

 RACSequence *sequence = [[models rac_sequence] map:^id(ChatViewModel *model) {
 return RACObserve(model, unread);

Nothing new here. RACSequence, map, RACObserve.

Note: In this case, we map our sequence of values to the sequence of signals.

Actually we don’t need so many signals, we need one, but a meaningful one, since our pieces of the constantly changing data have to be processed together. There are a few ways to combine signals in RAC and you can choose one that suits your needs.

+merge: for example, will forward the values from our signals into a single stream. It doesn’t exactly fit our needs as in the next block we’ll see only one latest value (YES or NO, in our case).

Since we want to know all values (in order to calculate the sum of them), let’s use  +combineLatest: It will watch our signals for changes, and then, send the latest values from all of the signals when a change occurs. In the next block we can see a “snapshot” of all our unread values.

[RACSignal combineLatest:sequence];

Now we can get an array of the latest values every time a single value changes. It’s almost done! The only task remained is to calculate how many times YES occurs in this array. We can do it with a simple loop, but let's be functional to the end and use reduce. Reduce is a well-known function in the functional programming which converts the collection of data into a single atomic value with a predetermined rule.  In RAC this function is -foldLeftWithStart:reduce: (or -foldLeftWithStart:reduce:).

[unreadStatuses.rac_sequence foldLeftWithStart:@0 reduce:^id(NSNumber *accumulator, NSNumber *unreadStatus) {
 return @(accumulator.integerValue + unreadStatus.integerValue);


The last thing that remains unclear is why do we need switchToLatest?

Without it we’ll get a signal of signals (as we map an array value to a signal) and if you subscribe to unreadStatusChanged, you’ll get a signal in the next block, not a value. We may use either -flatten or -switchToLatest (which is flattened but with a little difference) to fix this.

flatten means that a subscriber to the signal that is being flattened will receive the values sent by the signal which returned from the map. While -flatten takes a signal of signals and merges them together forwarding the values sent by any of them, -switchToLatest is doing the same thing, but it forwards the values just from the latest signal.

It works better for us since we don’t want to keep getting changes from the old version of our dataSource array. The signal is ready and we can try it, let’s make a side effect:

RAC([UIApplication sharedApplication], applicationIconBadgeNumber) = unreadStatusChanged;

Did you notice how a formulation of the problem is tied to the solution code? We’ve just written declaratively what we want to do in one place. We don’t need variables to save an intermediate state.

Sometimes you have to go deep in the framework documentation to find the right operators to formulate your custom signal, but it's totally worth it.

Task #2

Here is one more task that demonstrates the possibilities of the framework. We had a chats list screen in our app and the task was: open chats screen when one of the chats has received the first message. Here is what a resulting signal looks like:

RACSignal *chatReceivedFirstMessage = [[RACObserve(self, dataSource) map:^id(NSArray *chats) {
 RACSequence *sequence = [[[chats rac_sequence] filter:^BOOL(ChatViewModel *chat) {
 return ![chat hasMessages];
 }] map:^id(ChatViewModel *chat) {
 return [[RACObserve(chat, lastMessage) ignore:nil] take:1];
 }] ;

 return [RACSignal merge:sequence];
}] switchToLatest];

Let's see what it consists of.

This example is quite similar to the previous one. We observe an array, map values to the signal, and flatten the result taking into account only the latest signals.

At first we filtered our dataSource array in the map block because we aren’t interested in the chats that already have messages.

Then we map chat values to signals, again with the help of RACObserve.

return [[RACObserve(chat, lastMessage) ignore:nil] take:1];

Since the signal produced by RACObserve will fire with an initial value of the property which will be nil, we should ignore it. -ignore: operator is just what we need.

The second part of the task is to take into account only the first incoming message. -take: will take care of this. The signal will complete (and dispose) right after the first value is received.

Just to make things clear. There are three new signals we created in this code. The first one was created by RACObserve marco, the second one by calling -ignore: operator on the first newly created signal, and the third one by calling -take: on the signal created by -ignore:

Like in the previous example, we need one signal based on all of the created signals. We use -merge: to produce a new combined stream, as we don’t care about values like in the previous example.

Time to side effect!

 [chatReceivedFirstMessage subscribeNext:^(id x) {
 // switching to chat screen

Note: We don’t use values that come to the signal. However, x will contain a received message.

The education process is almost over. Now let’s talk a bit about the impressions.

What I really love about Reactive Cocoa

1. It’s easy to start. The framework is documented like crazy. There are so many examples on GitHub, detailed descriptions of each class and method, plenty of articles, slideshows, and video presentations.

2. You don’t need to complete change your style of programming. At first, you can use the existing solutions for the problems, such as UI binding, network wrappers, and others from Reactive Cocoa GitHub page. Then, step by step, you can grasp all the opportunities and methods of Reactive Cocoa.

3. It really changes your way of solving programming tasks from imperative to declarative. Now when the functional programming style is getting more and more popular in iOS community, it’s difficult for many to fundamentally change their way of thinking. Reactive Cocoa helps to make a change, because it has a lot of existing tools that will help you communicate in the style of "what to do" rather than "how to do".

What I don’t like about Reactive Cocoa

1. Wide use of macros (like RAC() or RACObserve()).

2. Sometimes it can be hard to debug as using RACSignals leads to deep stack traces.

3. Non type-safe (you never know what type to expect in the block subscribeNext). The best decision, in this case, is to document signals in the public interface, like this:

* Returns a signal which will send a User and complete or error.
-(RACSignal *)loginUser;


I can’t but mention Swift as well

Reactive Cocoa is written in Objective-C and for Objective-C specifically. But of course, now when Swift is gaining popularity, developers of the framework are not sitting around doing nothing. They are actually writing Swift APIs to use with Reactive Cocoa (The Great Swiftening is close). We are going to see a new 3.0 version with blackjack and hookers generics and operator overloading.

I'm sure that after that RAC will get even more fans. Soon perfectionists who curse macros and non-type-safety will have nothing to defend themselves with in the argument against Reactive Cocoa.


The functional reactive approach can simplify the way you make solutions in your daily tasks. Perhaps, at first, the concept of RAC may seem too complicated, its solutions too cumbersome, and bulky, and the number of operators will confuse you a lot. But later, it’ll become clear that all of this has a simple idea behind.

You represent your data (input or output) as a Stream. The Stream is like a pipe of events (data, error, complete). Signal and Sequence are a Stream. Signal is a push driven stream: when it has something, it’ll push this to you -- networking, user input, async loading from a disk, for example. Sequence is a pull driven stream: when you need something, you’ll pull this from a sequence -- collections, for example. Everything else is operators for transforming and combining all this stuff together (map, filter, merge, switchToLatest, throttle etc).

Also, you have to remember that all events are delivered to the subscribers on the threads in which they occurred. If you need to specify that, apply a given RACScheduler (a class similar to GCD queue but with a possibility for cancellation) by using the operator -deliverOn:

Usually you need to specify explicitly only [RACScheduler mainThreadScheduler] for updating an interface, but you can write your own implementation of RACSceduler when you deal with something specific, like CoreData with its contexts.


  1. Reactive Programming on Objective-C -- our article, where you can find a visual representation of the principles of work of the main operators.
  2. Reactive Cocoa on GitHub, where you can find the description of the main classes, operators, a lot of examples and design guidelines.
  3. Functional Reactive Programming on iOS -- so far the only book about RAC :)
  4. Reactive Cocoa Turorial -- a two-part detailed tutorial from Ray Wenderlich. Perfect for beginners.
  5. OctoKit - an open source project with a great example of a reactive network client.

Also check out:



Some lessons about CoreData migration and experiments with FastEasyMapping, our own library for mapping JSON to objects

8 Tips on CoreData Migration
Written by Dima Vorona

8 Tips on CoreData Migration

Iterative app development always entails CoreData migration. Working with iterative development, we’ve le...

From JSON to Core Data Fast
Written by Dima Vorona

From JSON to Core Data Fast

In order to import data from JSON to Core Data there exist quite a number of ready solutions. However, ...

Performance tuning

How do you make things run faster? Check out our experiments

From JSON to Core Data Fast
Written by Dima Vorona

From JSON to Core Data Fast

In order to import data from JSON to Core Data there exist quite a number of ready solutions. However, ...

Is Swift Faster Than Objective-C?
Written by Stas Kirichok

Is Swift Faster Than Objective-C?

Little time has passed since Swift 1.0 was released especially once you compare it with the lifetime of...

Mastering UIKit Performance
Written by Eugene Goloboyar

Mastering UIKit Performance

Even though a lot of articles and WWDC sessions are devoted to UIKit performance, this topic still rema...

Reactive programming

Our experiments in bringing declarative style into Objective-C

Reactive Programming on Objective-C
Written by Dima Vorona

Reactive Programming on Objective-C

Languages constantly change and evolve with time due to new technology appearing, modern requirements o...

How I Learned to Write Custom Signals in Reactive Cocoa
Written by Anastasiya Gorban

How I Learned to Write Custom Signals in Reactive Cocoa

Honestly, I started using Reactive Cocoa because it’s fashionable. I hear developers talking about this...

Best iOS coding practices

Check these out to make your code much better

Lightweight iOS View Controllers
Written by Alexey Chernish

Lightweight iOS View Controllers

When developing applications on iOS, View Controllers tend to become cumbersome and confusing which cau...

Declarative Navigation Bar Appearance Configuration
Written by Dima Vorona

Declarative Navigation Bar Appearance Configuration

The last couple of month I’ve been working on an app that required implementation of color changing beh...

See what else we can do

Check out our knowledge and capabilities

Let's talk code