Simple Network Prototyping in Objective-C

Article summary

I find that when writing applications for Mac OS and especially iOS, it’s quite common to need to interface with a simple REST web service that delivers JSON payloads. Unfortunately, Cocoa’s networking APIs can be a bit cumbersome. To address this for my own needs, I wrote a simple asynchronous wrapper around NSURLConnection a few years ago. As needed, I’ve evolved it to take advantage of new Objective-C features, and I’m finally making it available on github in the hope that others may find it useful.

A quick list of features:

  • Asynchronous transfer
  • Block-based API
  • Automatically parses and encodes JSON
  • Compatible with Mac OS & iOS

Note that it’s mostly meant for prototyping, so consider yourself warned about using it in release software. For example, error handling could be improved, and there are no logic tests.

Why?

NSURLConnection and its related classes are powerful, yet require a significant amount of code in order to use. Its interface provides you the response information and chunks of data as it receives them, which places the burden of reassembling the raw response data, determining whether any errors occurred, and processing the raw data upon your code. Typically this involves subclassing NSURLConnection or creating another object that handles its many delegate methods.

Without FCSwitchboard, the process of retrieving JSON via HTTP looks like this:

  • Create NSURLRequest and NSURLConnection objects for the URL you would like to fetch
  • Implement and create a delegate object for NSURLConnection
  • Write a delegate method to read the NSHTTPURLResponse
  • Write another delegate method to be notified of an error
  • Write another delegate method to receive chunks of data from the connection
  • Add yet another delegate method to be notified when the connection finished loading
  • Assemble the chunks of data, and, finally
  • Parse the JSON string with SBJSON or NSJSONSerialization

It’s a heavy price to pay, particularly when the extra power and flexibility is not needed. FCSwitchboard was built to do all of this for you.

Getting Started

To begin, you simply need to add the FCSwitchboard.h and FCSwitchboard.m files to your project. The second step is to subclass FCSwitchboard, in order to provide a nicer interface to the rest of your application and tell FCSwitchboard how to fetch data. Each subclass must specify a base URL upon which all requests are based. Here’s an example:

@interface FCTwitterSwitchboard : FCSwitchboard

- (id <FCSwitchboardConnection>)publicTweetsWithBlock:(void (^)(NSArray *, NSError *))block;

@end



@implementation FCTwitterSwitchboard

- init
{
    if ((self = [super init])) {
        [self setBaseURL:[NSURL URLWithString:@"http://twitter.com"]];
    }
    return self;
}

- (id<FCSwitchboardConnection>)publicTweetsWithBlock:(void (^)(NSArray *, NSError *))block
{
    return [self sendRequest:@"GET" forPath:@"statuses/public_timeline.json" withData:nil block:block];
}

@end

In order to use your subclass from the rest of your application, don’t instantiate it directly. The +[FCSwitchboard switchboard] method will automatically manage creating singletons for its subclasses. To continue the twitter example:

- (IBAction)logTweets:(id)sender
{
    [[FCTwitterSwitchboard switchboard] publicTweetsWithBlock:^(NSArray *tweets, NSError *error) {
        if (error) {
            NSLog(@"got error: %@", error);
            return;
        }
        for (NSDictionary *tweetInfo in tweets) {
            NSLog(@"%@: %@", [tweetInfo valueForKeyPath:@"user.screen_name"],
                  [tweetInfo valueForKey:@"text"]);
        }
    }];
}

That’s all there is to it. Note that any block you pass in to FCSwitchboard will get copied off the stack, so be careful about creating retain cycles.