NSOperation-WebFetches-MadeEasy now FastEasyConcurrentWebFetches

After reading the latest Apple Guides on NSOperation and Concurrency, I updated NSOperation-WebFetches-MadeEasy to use normal NSOperations. It simplified the code for sure. But then I asked myself why I was still using NSOperation and NSOperationQueue, and not GCD alone.

Since this established project has NSOperation in its title, I didn’t feel right in just updating it to GCD, so I cloned it to FastEasyConcurrentWebFetches and have now pushed that code up.

The big difference is that now is that most everything uses blocks and GCD. It turns out you can add a NSRunLoop to a block, and get the same ability to “sleep” awaiting messages as you do with NSOperation.

Screen Shot 2013-04-23 at 4.15.15 PM

This code will be the basis of a library I’m doing for my current employer, so it will get regular updates (as did NSOperation-WebFetches-MadeEasy), as bugs or other issues become evident.

NSOperation-WebFetches-MadeEasy

In late 2011 I found the need to use the same concurrent NSOperation feature, but in a class not descended from UIViewController. I had two choices: either duplicate the NSOperations code in a second subclass, or find a way to extract it into something I could re-use anywhere.

I choose the second approach, and created OperationRunner, a helper class that I use to this day. In the end, this helper class can be incorporated into any Objective-C class by taking 6 or so simple steps as outlined in the OperationsRunner.h file. This involves adding imports for this header file and another header with a single protocol callback method. The actual operations are subclasses of a single NSOperation subclass, ConcurrentOperation. A further subclass, WebFetcher, adds web functionality. [In my current code I have a half dozen or so such subclasses.] I created this code on my own time, and thus can incorporate it elsewhere as I see fit as I own it.

The helper class provides a small set of methods:

  • (id)initWithDelegate:(id <OperationsRunnerProtocol>)del;
  • (void)runOperation:(NSOperation *)op withMsg:(NSString *)msg;
  • (NSSet *)operationsSet;
  • (NSUInteger)operationsCount;
  • (void)cancelOperations;
  • (void)enumerateOperations:(void(^)(NSOperation *op)) b;

Operations are submitted with the runOperation and an optional debug message, which can get logged when the operations gets submitted to the NSOperationsQueue, and also when it completes. This is quite handy during debugging, and in my code I always supply a string, but suppress logging if the build is Deployment.

The completed operation is returned to the caller object via a delegate message:

– (void)operationFinished:(NSOperation *)op;

Within that message, the OperationsRunner object can report back whether this is the last operations, or others are still pending (they might even be all complete but have not been fed back via the delegate message.)

Queued operations can be cancelled at any time, and the whole stack quickly and gracefully collapses—cancelled operations do not result in delegate callbacks either.

My code has made extensive use of this class, and actually uses the exact code found in the NSOperation-WebFetches-MadeEasy repository. My last App Store App, Lot18, made extensive use of it, and often had hundreds of html and image fetches going on in multiple instances of the OperationsRunner. Refinements made at this time included the ability to vary the maximum number of concurrent operations, and the priority of the serial dispatch queue used to manage the operations. This architecture let the UI be responsive even in the face of a huge amount of background work (and the app has a 5 star rating!)

I am aware of many other networking frameworks, but mine does not try to be all inclusive—it does one thing really well—and it does what it aims to do quite well (YMMV). It’s something I’m very proud of, and I hope others will find it useful.

Evolution of my asynchronous networking code

In 2010 I used synchronous NSURLConnection‘s, completely blocking the UI (time pressure). In early 2010, I was working on a pre-existing app the did use asynchronous connections, but would block the Back button of a UIViewController until all completed. This was something the product managers really wanted fixed.

My solution used concurrent NSOperations (of which I’d had some prior experience). Using the “cancel” feature of operations, it was possible to stop all background operations when the user clicked the Back button and cleanup. It turns out there are many “corner” cases in the design—for instance, the user cancels before a queued operation has started running. Since the app also showed a spinner when the operations were running, the feature had to let the controller discover how many operations remained, in particular if all operations were complete.

Thus, in late winter, 2011, I had coded this feature into a UIViewController subclass, it was working pretty well, and I took what I’d learned and created an open source testbed for concurrent NSOperations on github, Concurrent_NSOperations, supporting both Mac and iOS targets.

This project allowed me to really flesh out the logic – the UI lets you choose the sequence of events (e.g., cancel before operations starts), and it let me catch and deal with many edge cases.