Rapture In Venice, LLC

:: Freelance iOS Development & Team Augmentation

InnerBand Tutorial Part 3: The Magical Message Center

This is the third part of a multi-part series on the InnerBand Framework. Previously, in InnerBand Tutorial Part 2: Core Data Quick and Easy!, I reviewed the Core Data Store and how it makes working with Core Data actually…kinda fun!

From the community of InnerBand developers, I’ve heard nothing but compliments and happiness about what it does for them. They love the macros and functions that keep their code concise, the category methods that are so useful they can’t imagine living without them, the Core Data Store which brings them sanity, and then classes like IBAlertView which demonstrate why blocks are so darn awesome. I love that they love InnerBand, but there’s one piece of it that they never seem to mention: Message Center.

I can’t blame them. For one thing, it’s never been documented. Second, if any enterprising developer did read analyze the code, they would likely be left with no motivation to use it. Is that assessment fair? NO.

So, what does it do then?

What Is the Message Center?

On the face of it, the Message Center is very similar to the NSNotificationCenter provided by Apple. It allows objects to listen for notifications (dispatch messages) from other objects, send them, include message-specific information by way of an NSDictionary, and stop listening as well. The familiarity is intentional so that other iOS developers would pick up on it quickly.

Let’s see how the basics work. As an example, let’s presume we have two objects, myWeatherWidget and myWeatherReporter. When myWeatherReporter updates the weather forecast, it notifies its listeners. When myWetherWidget gets the notification, it updates its UI to reflect the new information. Got it?

Here’s the most basic way myWeatherReporter can notify its listeners:

[MessageCenter sendGlobalMessageNamed:MSG_FORECAST_UPDATED];

The MessageCenter is used in static form; no need to access a singleton or defaultCenter. This particular code dispatches a global message so that you only need to provide the name, but you can provide the source object so listeners can be more discriminating. Everything else is done for you. If you were using the NSNotificationCenter, you’d of had to specify the source object as nil to get the “global” behavior. Note, also, that MSG_FORECAST_UPDATE is any arbitrary, unique string (Personally, I tender to define it as the same name as the constant).

If we want to pass along the weather information along with the message, we’d do it like this:

[MessageCenter sendGlobalMessageNamed:MSG_FORECAST_UPDATED withObjectsAndKeys:weatherInfo, @"info", nil];

There are actually several versions of the above you could use depending on your style preference, such as with sendGlobalMessageNamed:withUserInfoKey:andValue:, but they all do the same thing which is to package data in the message’s userInfo property.

For an object to receive the message, it must register itself as a listener and provide a method:

- (void)viewDidLoad {
    [super viewDidLoad];
    [MessageCenter addGlobalMessageListener:MSG_FORECAST_UPDATED target:self action:SEL(forecastUpdated:)];
}

- (void)forecastUpdated:(DispatchMessage *)msg {
    NSDictionary *userInfo = msg.userInfo;
    ...
}

Self-explanatory. Again, you don’t have to make these global messages if you want to be more specific. Also, you don’t have to accept the DispatchMessage argument if you don’t need it. Remember, too, that your objects should remove themselves as a listener before being deallocated. I tend to make the object add itself as a listener in viewDidLoad and unregister accordingly:

- (void)viewDidLoad {
    [super viewDidLoad];
    [MessageCenter addGlobalMessageListener:MSG_FORECAST_UPDATED target:self action:SEL(forecastUpdated:)];
}

- (void)viewDidUnload {
    [MessageCenter removeMessageListenersForTarget:self];    
    [super viewDidUnload];
}

- (void)dealloc {
    [MessageCenter removeMessageListenersForTarget:self];    
}

In 99% of cases, when you unregister a listener you mean to unregister for everything the object is listening to (i.e., the object is going away). If you want to stop listening to just one type of message, there are plenty of methods for handling that case such as removeMessageListener:target:action:.

This is the basic Observer pattern as Message Center implements it. Minus a little more conciseness, you already have that with NSNotificationCenter. Now, let’s see why you’ll never want to use NSNotificationCenter ever again!

What Makes Message Center Awesome?!

What makes Message Center superior to NSNotificationCenter is that it helps you get your real-world work done much faster. For this example, I’ll be referencing the Movie Poster Fetcher which you can download and use as inspiration. What this little app does is let you enter in the name of your favorite movie and it will display the movie poster and some other information. Seems simple enough, but keep in mind that an app like this has a few components you’d normally have to deal with:

  • You need to construct the GET request to get the movie poster information.
  • You need to create handlers for receiving the response as well as the streaming data.
  • You need to spin a thread that will process the response.
  • You need to handle errors.
  • You need to notify listeners that you have the new data ready.
  • You need to display that information.

Everyone, and I mean everyone, writes this code differently. If you like to use NSNotificationCenter, you might notify once when the data comes in and then that listener will notify a second time when it’s done processing it. You might use an asynchronous NSURLRequest, NSOperationQueue, dispatch queues, ASIHTTP, SVHTTPRequest, or any number of other methods. It really makes for a great interview challenge, eh?

Here’s how you would do it with the Message Center:

- (void)requestMoviePosterForTitle:(NSString *)title {
    NSString *url = @"http://www.imdbapi.com/?i=&t=[TITLE]&r=XML";
    NSDictionary *userInfo = [NSDictionary dictionaryWithObjectsAndKeys:title, @"TITLE", nil];

    __block HTTPGetRequestMessage *get = [HTTPGetRequestMessage messageWithName:MSG_POSTER_RECEIVED userInfo:userInfo url:url processBlock:^(NSData *stream, NSInteger httpResponse) {
        if (httpResponse == 200) {
            RXMLElement *rxml = [RXMLElement elementFromXMLData:stream];

            // process xml and supply it to the listeners
            if ([rxml isValid]) {
                RXMLElement *movieElement = [rxml child:@"movie"];
                
                [get setUserInfoValue:[movieElement attribute:@"title"] forKey:@"title"];
                [get setUserInfoValue:[movieElement attribute:@"poster"] forKey:@"poster"];
            }
        }
    }];
    
    // dispatch
    [MessageCenter sendGlobalMessage:get];
}

Wait, wait…WHOA, WHOA. Stop right there! I’ve seen that look before. This is the part of the tutorial where you’re disappointed to see all this unfamiliar, fairly bulky code and you just lose confidence that this is the framework you’ve been looking for all your life. Well, no, no, no, you keep reading! Look at the code again! This code does everything. It’s performing the HTTP GET request, it’s checking the response, it’s even processing the XML! (Thanks to RaptureXML!)

Let’s review it line by line:

  • Lines 2 – 3 – We define the URL with parameters that will be substituted automatically. In this case, [TITLE] will be replaced by the title string we provide. In the real world, the URL would be a constant, and this is a cleaner syntax to read than a string formatter. (But you’re free to do it yourself if you like. No biggie.)
  • Line 5 – We define the HTTP GET request. This is the magic! The request is a message itself and it’ll process the request before dispatching to the listeners. Essentially, you’re telling this thing to do the request, and when it’s all done let the listeners know. This is remarkably different from what you usually see which is a message simply having a name and no other utility.
  • Line 6 – This is the processing block. More magic! The idea here is that we’ll process the response immediately when it comes, and only when that’s been done do we notify the listeners. The reason we do it this way is because if we waited for a listener to process the XML, then we could only let one listener handle it. That, or every listener would have to process the XML itself because the order can’t be guaranteed!
  • Lines 7 – 12 – This is RaptureXML doing its thing. It tears apart the XML in a laughably tiny amount of code.
  • Lines 13 – 14 – Here’s where we take the two bits of information we care about (movie title and poster URL) and add them to the user info of the message. Every listener will now get this information! Note that while you can add information anytime you like, you shouldn’t really do it in the listeners themselves because the order can’t be guaranteed. However, in a processing block, it’s perfect! (Obviously, you can store the information in Core Data, too.)
  • Line 20 – This is where we dispatch the message. All the magic happens only after we dispatch. Remember, the processing block is called before the listeners get notified!

Look at this code and really think about it. There’s no wasted space. Everything does it’s job succinctly, efficiently, and clearly. And in this tiny amount of space, we’ve written all the web service integration code we need.

With Message Center, we can do HTTP POST as well. We can also define our own message subclasses, too, that we can re-use over and over again! Dump your JSONKit processing code in a message and have it automatically decoded for you en route to your listeners. Wrap sound effects in messages. Anywhere you take input and produce output you can create a message that will do it for you while you dispatch!

What Else Does Message Center Do?

If that’s all you got from Message Center, trust me, you’re gonna live a happy life. But there’s more, but I’ll offer it with one caveat: you might not ever need it. The reason is that most of this stuff preceded the process block style you see above. (InnerBand itself preceded blocks.) For example, a SequencedMessage lets you chain together a set of messages in such a way that the output of each message is fed into the input of the following message like a human centipede. How is this valuable? Think if you had a message that handled HTTP GET requests and a message that processed JSON. You could chain these together into one SequencedMessage, dispatch, have the HTTP GET request your data, have the JSON message process your data, and only then would all the listeners receive it without any of them having to handle the processing!

Cool, right? But since that’s the prototypical use case, processing blocks blow it away because there’s no need to subclass and all the code stays in one place. Still, the SequencedMessage lets you take advantage of DispatchMessage subclasses that you create and chain them together like high-tech legos. It’s good to be aware of.

While subclassing is great for chaining, also keep in mind that BlockBasedDispatchMessage alleviates even that. By providing your input and output handlers as blocks, you avoid the hassle and bulk of new classes. Be sure to check those out as well!

Summary

The Message Center is a similar, but more powerful, version of the NSNotificationCenter that fundamentally redefines the role of the message. Instead of just being a named thing, it’s a named thing that performs functionality before it dispatches to its listeners, either synchronously or asynchronously. Processing blocks serve as guaranteed, called-first code that empowers you to have multiple listeners without having to split the flow into two parts, one listener processing the response and the other listeners to update based on that new data.

In its most basic form, Message Center lets you loosely couple message sending between components of your application. In its most useful form, Message Center lets you send HTTP requests, process the responses, and then notify listeners of the data in one short, sweet step!

Again, take a look at the Movie Poster Fetcher and see the magic for yourself!

  • Print
  • Facebook
  • Twitter

John Blanco

John Blanco is a freelance iOS developer living in Lakewood, Colorado. He's been developing mobile apps for over 15 years, beginning in the medieval days of Java ME and Blackberry and all the way through iPhone and Android! He's led development on dozens of apps across a wide variety of domains such as retail, vision, orthotics, games, sports, and more!

More Posts - Website

Follow Me:
LinkedIn

, , ,

Comments are currently closed.

One thought on “InnerBand Tutorial Part 3: The Magical Message Center