I was recently working with Josh Twist on doing some iOS development and we ran into something that neither of us hadn’t done before.  Specifically, we were doing some network requests and wanted to be able to pass in a block to the class doing the NSURLConnection and have that block called when the connection finished.  Josh and I managed to get this to work and he’s detailed how to do that here.  I won’t cover everything that Josh already has but will go over this briefly so that I can explain how to use separate blocks for each URL Connection. 

What is a block?
That’s a more complicated question than I’m ready to fully answer so instead, I’ll summarize.  A block is a chunk of code that can be passed around (like a variable).  This code can then be executed later on.  So, what we want to do is pass a block of code to the class doing our URL Connection and have that code called when the connection finishes.  If you want a more detailed explanation of Blocks, read Apple’s iOS Blocks documentation.

The Problem
URL Connection is (primarily) used to make asynchronous requests.  That is to say that when you make a call to initWithRequest on your URLConnection, it kicks off the process.  The method call initWithRequest takes in a delegate which is used to call all of the URLConnection methods to process results of the request.  So, if you want to pass a block in you need to store this somewhere so that when the delegate method (didFinishLoading) is called, you can then access the block.  As I said, Josh and I figured out how to do this and he’s thoroughly explained it in his blog.  As Josh mentions he created is not threadsafe and shouldn’t be used for multiple requests.  The reason for this is that if you call the class that does the URL Connection too quickly, the block storage will be overwritten, as will the receivedData object.  What we want to do is store both the block and the receivedData object for each connection we create and then when the connection finishes, pull out the objects specific to that connection.  Thankfully, we can do that. 

We’ll accomplish this in two steps.  First, we’ll look at storing multiple callback blocks instead of just one.  Once we’ve done that, we’ll store a different receivedData object for each connection and use the correct one for the right connection.

Step One:  Storing Multiple Callback Blocks
The first thing you need to do is add a NSMutableDictionary to your connection class’ @private members.  If you’re going from Josh’s example (http://www.thejoyofcode.com/iOS_blocks_and_HTTP_requests.aspx), the JoshRequest class matches up with my ServiceCaller class:

ServiceCaller.h
@interface ServiceCaller : NSObject <NSURLConnectionDelegate> {
   @private 
       NSMutableData* _receivedData;
       NSMutableDictionary* callbacks;
}
-(ServiceCaller*) init;
-(void) postToUrl: (NSString*) url withBody: (NSData*) body
    withCallback: (void(^) (NSString* response)) callback;
@end

This is the dictionary we’ll use to store our callback objects in.  Now the only thing you need to change in the postToUrl method is how you store the callback:

ServiceCaller.m
-(ServiceCaller*) init {
   callbacks = [[NSMutableDictionary alloc] init];
   return self;
}
...
-(void) postToUrl:(NSString *)url withBody:(NSData *)body 
    withCallback: (void (^)(NSString *))callback {
   NSMutableURLRequest* request = [NSMutableURLRequest 
                                   requestWithURL: [NSURL URLWithString:url]];
   [request setHTTPMethod:@"POST"];    
   NSError *error;
   // should check for and handle errors here
   [request setHTTPBody:body];
   NSURLConnection *conn = [[NSURLConnection alloc] 
                            initWithRequest: request delegate:self];
   _receivedData = [NSMutableData data];
   [callbacks setValue:callback forKey:[NSString stringWithFormat:@"%i", conn.hash]];
}
...
Note here that for a key, we’re using the hash of the URLConnection object.  The hash variable is on NSObject and is used as a table address in a hash table structure.  I’m no programming language / OS expert, but from my testing so far, I haven’t run into two different URLConnections that have the same value.  It would seem this value should be unique (at least in terms of a client side application that isn’t doing that many posts).  As an alternative, you could always subclass the NSURLConnection class and add some sort of a Tag field where you can store your own key value (based off of a counter perhaps).
Now in the connectionDidFinishLoading method, you pull the block back out of the dictionary and execute it.

ServiceCaller.m (cont.)
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{   
   NSString *txt = [[NSString alloc] initWithData:_receivedData
                                         encoding: NSASCIIStringEncoding];
   NSString* connectionHash = [NSString stringWithFormat:@"%i", connection.hash];
   id test = [callbacks valueForKey:connectionHash];
   void (^ callbackBlock)(NSString *response) = [callbacks valueForKey:[NSString stringWithFormat:@"%i", connection.hash]];
   callbackBlock(txt);
   [callbacks removeObjectForKey:connectionHash];
}

Make sure you remove the record from the dictionary after you use it.  Otherwise, you’ll be left with a memory leak.

Step Two:  Storing Received Data Per Connection
What we’ve got is great, but it’s still not thread safe.  The receivedData object is still being instantiated once and could be overwritten if we use the ServiceCaller class several times fast enough or have a slow response (which is entirely possible if the device is getting a poor signal).  To solve this problem, instead of just storing a callback in the dictionary, we’ll make a custom object and store that there.  Go ahead and add a new object that inherits from NSObject:

StateObject.h

@interface StateObject : NSObject
   @property (nonatomic, copy) void (^ callbackBlock)(NSString *response);
   @property (nonatomic, retain) NSMutableData* receivedData;
@end

Pretty simple, we’re storing the same callback block that we were storing before in ServiceCall in addition to a receivedData object as mentioned before.

StateObject.m

@implementation StateObject
   @synthesize receivedData;
   @synthesize callbackBlock;
@end

Very little to this as well.  Now let’s look at the changes to ServiceCaller.m (the .h will stay the same (though you can remove the receivedData private field).

ServiceCaller.m

-(void) postToUrl:(NSString *)url withBody:(NSData *)body 
    withCallback: (void (^)(NSString *))callback {
   NSMutableURLRequest* request = [NSMutableURLRequest 
                                   requestWithURL: [NSURL URLWithString:url]];
   [request setHTTPMethod:@"POST"];    
   NSError *error;
   // should check for and handle errors here
   [request setHTTPBody:body];
   NSURLConnection *conn = [[NSURLConnection alloc] 
                            initWithRequest: request delegate:self];
   StateObject* connectionState = [[StateObject alloc] init];
   connectionState.receivedData = [[NSMutableData alloc] init];
   [connectionState.receivedData setLength:0];
   connectionState.callbackBlock = callback;
   [callbacks setValue:connectionState forKey:[NSString stringWithFormat:@"%i", conn.hash]];
}

The bulk of this method is the same.  The changes come in after the request connection is initialized.  Now, we’re initializing a new StateObject and setting its properties.  Note that we’re initializing the receivedData here and not in the didReceiveResponse method which now looks like this:

-(void)connection:(NSConnection*)conn didReceiveResponse:
(NSURLResponse *)response 
{
}

The didReceiveData method changes to pull out the receivedData object for the specific connection and append data to it:

- (void)connection:(NSURLConnection *)connection didReceiveData:
(NSData *)data
{
   StateObject* connectionState = [callbacks objectForKey:[NSString stringWithFormat:@"%i", connection.hash]];
   [connectionState.receivedData appendData:data];
}

Lastly, the changes to connectionDidFinishLoading:

- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{   
   NSString* connectionHash = [NSString stringWithFormat:@"%i", connection.hash];    
   StateObject* connectionState = [callbacks objectForKey:connectionHash];    
   NSString *txt = [[NSString alloc] initWithData:connectionState.receivedData
                                         encoding: NSASCIIStringEncoding];    
   connectionState.callbackBlock(txt);
   [callbacks removeObjectForKey:connectionHash];
}

Here again, pulling the StateObject out and using its properties as well as removing it when we’re done.  Now you’ve got a class which, in theory, could be used for doing any of your data connections.  You just pass in the specific block of code for whatever connection you’re making.  One additional thing you could do is specify an error block to be called from the NSURLConnectionDelegate’s didFailWithError method.  This could be stored as an additional property on the StateObject. 

You can check out the source code from today here.


Chris Risner


Leave a Comment