Last week I read a tweet from Landon Fuller that showed the disassembly of +[NSURLConnection sendAsynchronousRequest:queue:completionHandler:] mainly as a simple call to +[NSURLConnection sendSynchronousRequest:returningResponse:error:] dispatched on a global queue.

I have never been a big fan of this asynchronous request API on NSURLConnection since it lacks a major feature in my opinion: cancellation.

Since writing a simple URL connection as an NSOperation subclass is not too hard, let’s go and build it!

First thing first, we will want our method look as close as possible to the one previously mentioned. We will thus provide a new method in a category on NSURLConnection

+ (id)sendActuallyAsynchronousRequest:(NSURLRequest *)request queue:(NSOperationQueue *)queue completionHandler:(void (^)(NSURLResponse *response, NSData *data, NSError *connectionError))completionHandler;

This method returns an opaque object that can be used in order to cancel the request by mean of another method

+ (void)cancelActuallyAsynchronousRequest:(id)connection;

The implementation is fairly simple: we create a URL connection operation with the request that we dispatch to a global queue. We then have a dependent operation that retrieves the connection’s results and invoke the completion handler. This operation is dispatched to the provided queue or the main queue if none is provided. Finally, the connection operation is returned.

+ (id)sendActuallyAsynchronousRequest:(NSURLRequest *)request queue:(NSOperationQueue *)queue completionHandler:(void (^)(NSURLResponse *response, NSData *data, NSError *connectionError))completionHandler
{
	queue = queue ? : [NSOperationQueue mainQueue];
	completionHandler = completionHandler ? : ^ (NSURLResponse *response, NSData *data, NSError *connectionError) {};

	_NSURLActuallyAsynchronousURLConnectionOperation *connectionOperation = [[_NSURLActuallyAsynchronousURLConnectionOperation alloc] initWithRequest:request];
	[[self _actuallyAsynchronousURLConnectionOperationQueue] addOperation:connectionOperation];

	NSOperation *completionOperation = [NSBlockOperation blockOperationWithBlock:^ {
		NSURLResponse *response = [connectionOperation response];
		NSData *data = [NSData dataWithData:[connectionOperation data]];
		NSError *connectionError = [connectionOperation error];

		completionHandler(response, data, connectionError);
	}];
	[completionOperation addDependency:connectionOperation];
	[queue addOperation:completionOperation];

	return connectionOperation;
}

Cancellation is also very easy since the opaque object we returned is the operation itself, we simply need to call cancel on it.

+ (void)cancelActuallyAsynchronousRequest:(id)asynchronousRequest
{
	NSParameterAssert([asynchronousRequest isKindOfClass:[_NSURLActuallyAsynchronousURLConnectionOperation class]]);

	_NSURLActuallyAsynchronousURLConnectionOperation *operation = (_NSURLActuallyAsynchronousURLConnectionOperation *)asynchronousRequest;
	[operation cancel];
}

The implementation of the _NSURLActuallyAsynchronousURLConnectionOperation operation is also fairly basic. This operation is concurrent so that we can benefit from the asynchronous nature of NSURLConnection.

We create a URL connection in the initializer and provide an operation queue as the connection delegate queue so that we don’t need to provide a runloop (usually by using the main runloop or by creating a connection thread running the runloop and that is shared by all the connection operations).

The implementation of start simply starts the connection. Similarly, the implementation of cancel cancels the connection and finish the operation.

We create a mutable data to which we append data each time it is received. This is not an ideal situation when dealing with large response bodies but it is the API provided by NSURLConnection and for the sake of this example we will try to mimic it as much as possible.

Any failure on the connection will be reported as an NSError to the delegate, in which case we store the error and finish the operation. Similarly, when the connection finishes loading we can nicely finish the operation.

Have a look at the complete implementation of the operation on GitHub.

And that’s it! As you can see, providing a simple asynchronous and cancelable asynchronous request built as an operation on top of NSURLConnection is not so hard.