Skip to content
robbiehanson edited this page Nov 18, 2011 · 1 revision

What's new in version 3 of XMPP Framework

Introduction

Version 3 was officially released in June 2011. V3 was merged into the default branch, while the older v2 now has its own branch.

Version 3 requires Mac OS X 10.6+ or iOS 4.0+.

Motivation

Version 2 of the framework added quite a bit in terms of support for extensions. It provided a number of additional hooks for developers to use, and provided a module architecture to make writing extensions easier. I believe it was rather successful as evidenced by the number of additional extensions that were contributed to the project. However, version 2 was not without wrinkles. The biggest problem was performance. It's not necessarily that the core of the framework was slow. But after adding a number of extensions (like capabilities handling, roster support, MUC, etc) the framework would slow. This was due to the fact that everything was running on a single thread. I ran into performance problems myself. I recall a problem with large rosters (1,000+ users) which was causing me grief. In order to fix it, I had to move the roster handling onto another thread. It worked, but it certainly wasn't the easiest or most elegant solution. So one of the core problems of version 2 is that all extensions run on the same thread. And moving an extension onto another thread is not always an easy task.

And this was made even more difficult by the fact that the v2 xmpp framework wasn't thread-safe. So any calls to XMPPStream needed to go through the thread it's executing on...

Now I want to quickly point out that the latest Mac Pro (at the time of this writing) has 12 processor cores. 12! And the lowest end Mac has at least 2 processor cores. And even the new iPad has 2 processor cores.

So it seems to me that running an entire xmpp framework in a single thread (on only a single core, with maybe 11 left sitting idle) is rather silly.

What's New Overview

GCD based AsyncSocket

  • Thread-safe
  • Benchmarked up to 400% faster than v2 RunLoop version

XML parsing in its own dedicated queue

  • Won't block XMPPStream

Thread-safe XMPPStream

  • Feel free to send elements from any thread you want

Every XMPPModule can now be run in its own queue

  • Parallel execution of extensions

Every XMPPStream delegate can now be run in its own queue

  • Parallel execution of delegates

Dedicated logging framework

  • GCD based
  • Extremely fast, powerful and flexible
  • Log statements can easily be sent anywhere (console, file, database, combinations, etc)
  • Logging can optionally be asynchronous

To sum up the changes: Massive parallel execution!

What's New Detail

The first thing you'll notice when you upgrade to v3 is the XMPPStream delegate pattern, which now looks like this:

- (void)addDelegate:(id)delegate delegateQueue:(dispatch_queue_t)delegateQueue;
- (void)removeDelegate:(id)delegate delegateQueue:(dispatch_queue_t)delegateQueue;
- (void)removeDelegate:(id)delegate;

So you specify the dispatch queue that the XMPP framework should invoke your delegate methods on. If you wanted that to be on the main thread, you could simply do this:

[xmppStream addDelegate:self delegateQueue:dispatch_get_main_queue()];

However, you can trivially parallelize your code by creating a separate dispatch queue to handle processing of various elements.

This parallelism also extends to every single module. You'll notice that the XMPPModule class has received an overhaul. The first change is the default init methods:

@interface XMPPModule : NSObject
{
    ...
}

- (id)init;
- (id)initWithDispatchQueue:(dispatch_queue_t)queue;

This gives you full control over the parallelism of your modules. You may choose to run every module in their own queue. Or perhaps you have several modules that you want to share the same NSManagedObjectContext, in which case you could run those modules on the same dispatch queue. It's all up to you.

Another change made to XMPPModule is a more explicit activation/deactivation of modules:

- (BOOL)activate:(XMPPStream *)xmppStream;
- (void)deactivate;

This provides proper thread-safety for modules (as documented extensively in XMPPModule.m).

As mentioned earlier, XMPPStream is now thread-safe. So you can invoke methods on it from any thread/queue safely.

Also new in v3 is the XMPPElementReceipt. This replaces the old synchronouslySendElement method, and provides more functionality. It may be optionally used for any element you wish to send:

- (void)sendElement:(NSXMLElement *)element;
- (void)sendElement:(NSXMLElement *)element andGetReceipt:(XMPPElementReceipt **)receiptPtr;

The XMPPElementReceipt class is simple and easy to use with only a single method:

@interface XMPPElementReceipt : NSObject
{
    ...
}

/**
 * Element receipts allow you to check to see if the element has been sent.
 * The timeout parameter allows you to do any of the following:
 * 
 * - Do an instantaneous check (pass timeout == 0)
 * - Wait until the element has been sent (pass timeout < 0)
 * - Wait up to a certain amount of time (pass timeout > 0)
 * 
 * ...
**/
- (BOOL)wait:(NSTimeInterval)timeout;

@end

This allows you to do something like this:

- (void)applicationWillResignActive:(UIApplication *)application
{
    // Send some element (whatever) that the server requires when our app is in this state.
    // Recall that the sendElement method is asynchronous.
    
    XMPPElementReceipt *receipt;
    [xmppStream sendElement:whatever andGetReceipt:&receipt];
    
    // Wait until the framework notifies us that our element has been sent.
    [receipt wait:-1];
}

You'll also notice there is a new dedicated logging framework. This brings a great deal of needed functionality. The XMPPLogging header defines 4 log levels:

XMPPLogError(@"Just like NSLog");
XMPPLogWarn(@"Also with args: %i", someInt);
XMPPLogInfo(@"Or with multiple: %i - %@", someInt, someObject);
XMPPLogVerbose(@"Seriously, just like NSLog");

And every single file within the framework gets its own log level:

// Log levels: off, error, warn, info, verbose
static const int xmppLogLevel = XMPP_LOG_LEVEL_VERBOSE;

This means that if you need to troubleshoot any particular file, you can simply up the verbosity of the log level of that individual file. So you won't be bombarded with a thousand log statements from other parts of the framework.

In addition to this, XMPPLogging defines a separate trace flag. Tracing is separate from the log levels. That is, you can set the log level to warn, and enable tracing. This allows you to quickly trace which methods are getting executed within the file, which is often handy when you just want to learn the call-paths, or see if something is getting executed.

There are a ton of cool things you can do with the logging framework. And it's all built atop CocoaLumberjack. To find out more, check out the Lumberjack Wiki Articles.

GCD

The parallelism, power, and thread-safety of v3 is accomplished using Grand Central Dispatch. For those familiar with GCD, you know that it scales excellently from 1 CPU core to 100. For those unfamiliar with GCD, let me tell you a little about my own experiences using it.

I first started using GCD when I was writing a logging framework named CocoaLumberjack. The primary requirement was that it be really, really fast, and it work on the iPhone. So at first, I wrote it using traditional multi-threading techniques. Then v4.0 of iOS brought GCD support. So I revisted the logging framework, and rewrote it using GCD. Not only was it much faster, but the code required was significantly reduced. GCD allowed me to replace dozens and dozens of lines of code with simple one-liners. It was impressive.

After this, I began rewriting AsyncSocket from scratch to use GCD. The result is GCDAsyncSocket, and I couldn't believe how much faster it was. I wrote a blog post concerning its impressive performance.

Then I decided to really put it to the test. I've also written an embedded HTTP server for Mac & iOS. (CocoaHTTPServer). An HTTP server sounded like an obvious candidate for GCD. So I ported the entire thing to be GCD based. Not only was it easy, but the performance was incredible. You can read my blog post to hear about the benchmarks.

And finally, I gave an entire presentation on GCD at a local iOS developers meetup. I posted the video on my blog, under the title "Multi-core iOS devices are coming. Are you ready?"