Multiple NSURLConnections from one ViewController
We built SignMeOut with a lot of help from the community – from some great blogs and to the divine stackoverflow.com . So the least we thought we could do is contribute back some of the things we’ve learnt to help other people create great apps.
So the first thing we wanted to share was one of the hard parts we had to tackle – making multiple NSURLConnection requests from one viewcontroller.
We’ve all been there. We want to make a bunch of asynchronous requests and keep updating our UI smoothly without bothering the user interaction. But we’ve only got one delegate that needs to handle everything! So here’s the solution we put together that works really well for us.
Step 1: Create class NSURLConnectionWithTag that subclasses NSURLConnection and that can be assigned a tag in it’s constructor call.
Step 2: Create a dictionary of NSDatas in the target ViewController where your NSURLConnection delegate will be working and in your didReceiveData method add handling of the new data according to tag.
Step 3: Define and enum for all the connection types you want to handle
Step 4: Use the new constructor with each call to NSURLConnectionWithTag giving a defined enum value as the tag
Step 5: In your connectionDidFinishLoading method create a switch/case to handle the responses according to tags.
Ok that’s the general idea – let’s get to the code that get’s this done.
First of all we’ll need to create NSURLConnectionWithTag (Right Click->Add File , inherits from NSObject – the same old drill). Now you need to define a tag and then a constructor that assigns the tag when you call the asynchronous request. So your NSURLConnectionWithTag.h should look like this:
@interface NSURLConnectionWithTag : NSURLConnection {
NSNumber *tag;
}
@property (nonatomic, retain) NSNumber *tag;
- (id)initWithRequest:(NSURLRequest *)request delegate:(id)delegate startImmediately:(BOOL)startImmediately tag:(NSNumber*)_tag;
@end
now we’ll define our new method in NSURLConnectionWithTag.m:
- (id)initWithRequest:(NSURLRequest *)request delegate:(id)delegate startImmediately:(BOOL)startImmediately tag:(NSNumber*)_tag {
if ([[Reachability reachabilityForInternetConnection] currentReachabilityStatus] == NotReachable) {
// Always do some reachability handling
return nil;
}
self = [super initWithRequest:request delegate:delegate startImmediately:startImmediately];
if (self) {
self.tag = _tag;
}
return self;
}
Ok that gives us the class that we need. Now in the viewcontroller you’re going to be working in we’ll need to handle the different data streams that we’ll get. Define a dictionary that you can access later on in you delegate methods and init it
//Global
NSMutableDictionary *dataFromConnectionsByTag;
//In your init method
if (dataFromConnectionsByTag == nil) {
dataFromConnectionsByTag = [[NSMutableDictionary alloc] init];
}
This dictionary will associate a tag number with an NSData object. Now we’ll have to be organized to make sure we don’t kill our memory usage – so we’ll have to make sure to release and remove all the NSData objects we are done with. Your didReceiveData implementation should look like this:
- (void)connection:(NSURLConnectionWithTag *)connection didReceiveData:(NSData *)data{
if ([dataFromConnectionsByTag objectForKey:connection.tag] == nil) {
NSMutableData *newData = [[NSMutableData alloc] initWithData:data];
[dataFromConnectionsByTag setObject:newData forKey:connection.tag];
return;
} else {
[[dataFromConnectionsByTag objectForKey:connection.tag] appendData:data];
}
}
Now every connection will get it’s own NSData associated with it’s tag. Let’s already go ahead and make sure we don’t forget to handle our memory properly. At the bottom of your connectionDidFinishLoading (and of course also in connectionDidFail) add:
[[dataFromConnectionsByTag objectForKey:connection.tag] release]; [dataFromConnectionsByTag removeObjectForKey:connection.tag];
Great the hard work is over. Now we just need to make use of what we built. Set up an enum so it’s easy to keep track of your connections in a logical way
typedef enum {
DataConnectionSourceFirstPage = 0,
DataConnectionSourceSecondPage = 1,
DataConnectionSourceLastPage = 2,
...
} DataConnectionSourceTypes;
Now when you make your call to NSURLConnectionWithTag – do it with the new constructor with the appropriate tag (use any NSURLRequest you need). Like this:
[[NSURLConnectionWithTag alloc] initWithRequest:request delegate:self startImmediately:YES tag:[NSNumber numberWithInt:DataConnectionSourceFirstPage]];
And finally – add a switch/case to connectionDidFinish that does whatever you need after each connection completed.
NSString *resultHTML = [[NSString alloc] initWithData:[dataFromConnectionsByTag objectForKey:connection.tag] encoding:NSUTF8StringEncoding];
switch ([connection.tag intValue]) {
case <span style="font-family: Consolas, Monaco, 'Courier New', Courier, monospace; line-height: 18px; font-size: 12px; white-space: pre;">DataConnectionSourceFirstPage</span>
:{
//do something with resultHTML
}break;
case DataConnectionSourceSecondPage:{
//do something with resultHTML
}break;
...
That’s it! Now your view controller is ready to handle all the asynching you need!


Facebook comments: