Blocks Rock – A Cocoa Asynchronous NSURLConnection block example

So I heard that blocks were one of the new features of 4.1 and I decided to give it a try. And it’s awesome! Within half an hour I’d solved a problem that I’d had to make much more complicated implementations to solve previously, and I’m so excited I decided I’d share the code.

Problem: NSURLConnection asynchronous connections are ugly. If you want to have one controller make multiple different calls you have to make unique delegates for each call or have some convoluted way for the connection manager to tell one delegate from another. It’s a real mess.

Solution: BLOCKS!

Step 1: Create NSURLConnection block extension that allows for calling async methods from class API like you would sync method.


//
//  NSURLConnection-block.h
//
//  Created by Kevin Lohman on 9/12/10.
//  Copyright 2010 Logic High Software. All rights reserved.
//  Free to use in your code commercial or otherwise, as long as you leave this comment block in
// http://blog.logichigh.com/2010/09/12/cocoa-blocks/

#import 

@interface NSURLConnection (block)
#pragma mark Class API Extensions
+ (void)asyncRequest:(NSURLRequest *)request success:(void(^)(NSData *,NSURLResponse *))successBlock_ failure:(void(^)(NSData *,NSError *))failureBlock_;
@end

and

#import "NSURLConnection-block.h"

@implementation NSURLConnection (block)

#pragma mark API
+ (void)asyncRequest:(NSURLRequest *)request success:(void(^)(NSData *,NSURLResponse *))successBlock_ failure:(void(^)(NSData *,NSError *))failureBlock_
{
	[NSThread detachNewThreadSelector:@selector(backgroundSync:) toTarget:[NSURLConnection class]
						   withObject:[NSDictionary dictionaryWithObjectsAndKeys:
									   request,@"request",
									   successBlock_,@"success",
									   failureBlock_,@"failure",
									   nil]];
}

#pragma mark Private
+ (void)backgroundSync:(NSDictionary *)dictionary
{
	NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
	void(^success)(NSData *,NSURLResponse *) = [dictionary objectForKey:@"success"];
	void(^failure)(NSData *,NSError *) = [dictionary objectForKey:@"failure"];
	NSURLRequest *request = [dictionary objectForKey:@"request"];
	NSURLResponse *response = nil;
	NSError *error = nil;
	NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
	if(error)
	{
		failure(data,error);
	}
	else
	{
		success(data,response);
	}
	[pool release];
}


@end

Now just import your new class and make your async call! AWESOME!

	[NSURLConnection asyncRequest:request
						  success:^(NSData *data, NSURLResponse *response) {
							  NSLog(@"Success!");
						  }
						  failure:^(NSData *data, NSError *error) {
							  NSLog(@"Error! %@",[error localizedDescription]);
						  }];

14 thoughts on “Blocks Rock – A Cocoa Asynchronous NSURLConnection block example”

  1. I should comment… seems blocks are a little more work then I’d originally hoped. While this will work in some places, NSThread is a lot of overhead, and using a Singleton connection manager is probably a more efficient way to go. Additionally, when you are using values in blocks, it’s important to make sure they are in scope (make copies of parameter variables) and to check for the existence of those variables before calling them. Otherwise nasty, hard to track errors can occur with nested blocks.

  2. Hi, this is a great idea! It tidies up the code so much!

    However, I’ve tidied it up a bit further. Instead of creating an NSThread to do the work just use Grand Central Dispatch. The method below is all you need.

    + (void)asyncRequest:(NSURLRequest *)request success:(void(^)(NSData *,NSURLResponse *))successBlock_ failure:(void(^)(NSData *,NSError *))failureBlock_
    {
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    NSURLResponse *response = nil;
    NSError *error = nil;
    NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];

    if (error) {
    failureBlock_(data,error);
    } else {
    successBlock_(data,response);
    }

    [pool release];
    });
    }

  3. @Kevin Lohman: actually the blocks should be copied in only one case (but that’s a big case): if the block is supposed to be live beyond the scope in which it’s created (it’s the case in asynchronous operations like here), the block MUST be copied to the heap or you will get a crash for sure. And the code in this blog post is bound to crash.

    Here is the corrections, it’s at the dictionary creation:
    [NSDictionary dictionaryWithObjectsAndKeys:
    request,@”request”,
    [[successBlock_ copy] autorelease],@”success”,
    [[failureBlock_ copy] autorelease],@”failure”,
    nil]];

    Short explanation: blocks are allocated on the stack for optimization purpose, when the function returns the stack is cleaned up and the block dies out. The block has to be moved to the heap to exist beyond the scope, at that point it works like any other ObjC objects.

    When the block is still on the stack, -retain doesn’t do anything to the object, -retain must always return the receiver, that’s part of the semantic, thus sending retain to a block (like what NSDictionary is doing) won’t copy it to the heap. It has to be copied explicitly, you can then autorelease it because the dictionary will retain the block, in this case it works properly because -retain on a heap block increase its retain count just like normal ObjC objects.

  4. @Remy – Even better now… Using ARC (Auto retain / release, see XCode 4.2 documentation) the copy / autorelease nonsense is no longer required at all! (Celebration!)

  5. Misleading title. Making a Synchronous network call on background thread doesn’t make it asynchronous. You are blocking thread that makes synchronous request and waits for response, if the server takes several minutes to reply back your thread is blocked until then.

    1. Absolutely. 🙂 This was an example of making your own method with Blocks… At the time there wasn’t much on the web about them, but Apple is thankfully using it more and more (though the syntax can still get confusing and hard to write off the top of your head)

    1. FYI — Code here is mostly to demonstrate blocks, and is actually a really terrible way to do async connection handling (it spawns a new thread for EACH connection, bad memory management). A better solution is to use NSOperationQueue to handle it… or better still check out Apple’s NSURLSession (iOS developers only until iOS 7 goes live).

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s