NSURLConnection and grand central dispatch

I recommend you to see WWDC Sessions about network application in iPhone OS.

  • WWDC 2010 Session 207 – Network Apps for iPhone OS, Part 1
  • WWDC 2010 Session 208 – Network Apps for iPhone OS, Part 2

The lecturer said

“Threads Are Evil™”

for network programming and recommended to use asynchronous network programming with RunLoop. Use background thread (Grand Central Dispatch Concurrent Queue) for thread-safe data processing, not for network programming.

By the way, Blocks and Grand Central Dispatch sessions are also worth to see.

  • WWDC 2010 Session 206 – Introducing Blocks and Grand Central Dispatch on iPhone
  • WWDC 2010 Session 211 – Simplifying iPhone App Development with Grand Central Dispatch

I wrote a wrapper class for asynchronous NSURLConnection.


AsyncURLConnection.h

#import <Foundation/Foundation.h>

typedef void (^completeBlock_t)(NSData *data);
typedef void (^errorBlock_t)(NSError *error);

@interface AsyncURLConnection : NSObject
{
    NSMutableData *data_;
    completeBlock_t completeBlock_;
    errorBlock_t errorBlock_;
}

+ (id)request:(NSString *)requestUrl completeBlock:(completeBlock_t)completeBlock errorBlock:(errorBlock_t)errorBlock;
- (id)initWithRequest:(NSString *)requestUrl completeBlock:(completeBlock_t)completeBlock errorBlock:(errorBlock_t)errorBlock;
@end

AsyncURLConnection.m

#import "AsyncURLConnection.h"

@implementation AsyncURLConnection

+ (id)request:(NSString *)requestUrl completeBlock:(completeBlock_t)completeBlock errorBlock:(errorBlock_t)errorBlock
{
    return [[[self alloc] initWithRequest:requestUrl
        completeBlock:completeBlock errorBlock:errorBlock] autorelease];
}

- (id)initWithRequest:(NSString *)requestUrl completeBlock:(completeBlock_t)completeBlock errorBlock:(errorBlock_t)errorBlock
{

    if ((self=[super init])) {
        data_ = [[NSMutableData alloc] init];

        completeBlock_ = [completeBlock copy];
        errorBlock_ = [errorBlock copy];

        NSURL *url = [NSURL URLWithString:requestUrl];
        NSURLRequest *request = [NSURLRequest requestWithURL:url];
        [NSURLConnection connectionWithRequest:request delegate:self];
    }

    return self;
}

- (void)dealloc
{
    [data_ release];

    [completeBlock_ release];
    [errorBlock_ release];
    [super dealloc];
}

- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
    [data_ setLength:0];
}

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
    [data_ appendData:data];
}

- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
    completeBlock_(data_);
}

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
    errorBlock_(error);
}

@end

How to use AsyncURLConnection class.

/*
 * AsyncURLConnection -request:completeBlock:errorBlock: have to be called
 * from Main Thread because it is required to use asynchronous API with RunLoop.
 */

[AsyncURLConnection request:url completeBlock:^(NSData *data) {

    /* success! */

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

        /* process downloaded data in Concurrent Queue */

        dispatch_async(dispatch_get_main_queue(), ^{

            /* update UI on Main Thread */

        });
    });

} errorBlock:^(NSError *error) {

    /* error! */

}];

Leave a Comment