Managing multiple asynchronous NSURLConnection connections
صفحه اصلي

Managing multiple asynchronous NSURLConnection connections



I have a ton of repeating code in my class that looks like the following:

NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request                                                               delegate:self]; 

The problem with asynchronous requests is when you have various requests going off, and you have a delegate assigned to treat them all as one entity, a lot of branching and ugly code begins to formulate going:

What kind of data are we getting back? If it contains this, do that, else do other. It would be useful I think to be able to tag these asynchronous requests, kind of like you're able to tag views with IDs.

I was curious what strategy is most efficient for managing a class that handles multiple asynchronous requests.


Count the number of times a method is called in Cocoa-Touch?

1:

Scrolling to range in UITextView
I track responses in an CFMutableDictionaryRef keyed by the NSURLConnection associated with it. should we need to release viewcontroller's object after pushing into navigation controller?i.e.:. scrollview scrolls smooth in simulator but not in device?
connectionToInfoMapping =     CFDictionaryCreateMutable(         kCFAllocatorDefault,         0,         &kCFTypeDictionaryKeyCallBacks,         &kCFTypeDictionaryValueCallBacks); 
It may seem odd to use this instead of NSMutableDictionary although I did it for the reason this this CFDictionary only retains its keys (the NSURLConnection) whereas NSDictionary copies its keys (and NSURLConnection doesn't support copying).. Can we run Java applictions on iPhone? [closed] Once that's done:. What's a Java alternative to Google App Engine for developing iPhone Push Notification services?
CFDictionaryAddValue(     connectionToInfoMapping,     connection,     [NSMutableDictionary         dictionaryWithObject:[NSMutableData data]         forKey:@"receivedData"]); 
and now I have an "info" dictionary of data for each connection this I must use to track information around the connection and the "info" dictionary already contains a mutable data object this I must use to store the reply data as it comes in.. Running JavaScript in a UIWebView before any other JavaScript is run
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {     NSMutableDictionary *connectionInfo =         CFDictionaryGetValue(connectionToInfoMapping, connection);     [[connectionInfo objectForKey:@"receivedData"] appendData:data]; } 
UIScrollView image/photo viewer with paging enabled and zooming

2:

I have a project where I have two distinct NSURLConnections, and wanted to use the same delegate. What I did was create two properties in my class, one for each connection. Then in the delegate method, I check to see if which connection it is.
 - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {     if (connection == self.savingConnection) {         [self.savingReturnedData appendData:data];     }     else {         [self.sharingReturnedData appendData:data];     } } 
This also allows me to cancel a specific connection by name when needed..

3:

Subclassing NSURLConnection to hold the data is clean, less code than any of the another answers, is more flexible, and requires less thought around reference management..
// DataURLConnection.h #import <Foundation/Foundation.h> @interface DataURLConnection : NSURLConnection @property(nonatomic, strong) NSMutableData *data; @end  // DataURLConnection.m #import "DataURLConnection.h" @implementation DataURLConnection @synthesize data; @end 
Use it as you would NSURLConnection and accumulate the data in its data property:.
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {     ((DataURLConnection *)connection).data = [[NSMutableData alloc] init]; }  - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {     [((DataURLConnection *)connection).data appendData:data]; } 
That's it.. If you want to go further you must add a block to serve as a callback with just a couple more lines of code:.
// Add to DataURLConnection.h/.m @property(nonatomic, copy) void (^onComplete)(); 
Set it like this:.
DataURLConnection *con = [[DataURLConnection alloc] initWithRequest:request delegate:self startImmediately:NO]; con.onComplete = ^{     [self myMethod:con]; }; [con start]; 
and invoke it when loading is finished like this:.
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {     ((DataURLConnection *)connection).onComplete(); } 
You must extend the block to accept parameters or just pass the DataURLConnection as an argument to the method this needs it within the no-args block as shown.

4:

THIS IS NOT A NEW ANSWER. PLEASE LET ME SHOW YOU HOW I DID. To distinguish different NSURLConnection within same class's delegate methods, I use NSMutableDictionary, to set and remove the NSURLConnection, using its (NSString *)description as key.. The object I chose for setObject:forKey is the unique URL this is used for initiating NSURLRequest, the NSURLConnection uses.. Once set NSURLConnection is evaluated at .
-(void)connectionDidFinishLoading:(NSURLConnection *)connection, it must be removed from the dictionary.  // This variable need be able to be referenced from - (void)connectionDidFinishLoading:(NSURLConnection *)connection NSMutableDictionary *connDictGET = [[NSMutableDictionary alloc] init]; //...//  // You must use any object this must be referenced from - (void)connectionDidFinishLoading:(NSURLConnection *)connection [connDictGET setObject:anyObjectThatCanBeReferencedFrom forKey:[aConnectionInstanceJustInitiated description]]; //...//  // At the delegate method, evaluate if the passed connection is the specific one which needs to be handled differently if ([[connDictGET objectForKey:[connection description]] isEqual:anyObjectThatCanBeReferencedFrom]) { // Do specific job for connection //  } //...//  // When the connection is no longer needed, use (NSString *)description as key to remove object [connDictGET removeObjectForKey:[connection description]]; 

5:

One approach I've taken is to not use the same object as the delegate for each connection. Instead, I create a new instance of my parsing class for each connection this is fired off and set the delegate to this instance..

6:

Try my custom class, MultipleDownload, which handles all these for you..

7:

I usually create an array of dictionaries. Each dictionary has a bit of identifying information, an NSMutableData object to store the response, and the connection itself. When a connection delegate method fires, I look up the connection's dictionary and handle it accordingly. .

8:

One option is just to subclass NSURLConnection yourself and add a -tag or similar method. The design of NSURLConnection is intentionally very bare bones so this is perfectly acceptable.. Or perhaps you could create a MyURLConnectionController class this is responsible for creating and collecting a connection's data. It would then only have to inform your main controller object once loading is finished..

9:

in iOS5 and above you must just use the class method sendAsynchronousRequest:queue:completionHandler:. No need to keep track of connections since the response returns in the completion handler..

10:

I like ASIHTTPRequest..

11:

As pointed out by another answers, you should store connectionInfo any where and look up them by connection. . The most natural datatype for this is NSMutableDictionary, although it cannot accept NSURLConnection as keys as connections are non copyable.. Ananother option for using NSURLConnections as keys in NSMutableDictionary is using NSValue valueWithNonretainedObject]:.
NSMutableDictionary* dict = [NSMutableDictionary dictionary]; NSValue *key = [NSValue valueWithNonretainedObject:aConnection] /* store: */ [dict setObject:connInfo forKey:key]; /* lookup: */ [dict objectForKey:key]; 

12:

I decided to subclass NSURLConnection and add a tag, delegate, and a NSMutabaleData. I have a DataController class this handles all of the data management, including the requests. I created a DataControllerDelegate protocol, so this individual views/objects must listen to the DataController to find out when their requests were finished, and if needed how enough has been downloaded or errors. The DataController class must use the NSURLConnection subclass to start a new request, and save the delegate this wants to listen to the DataController to know when the request has finished. This is my working quick fix in XCode 4.5.2 and ios 6.. The DataController.h file this declares the DataControllerDelegate protocol). The DataController is also a singleton:.
@interface DataController : NSObject  @property (strong, nonatomic)NSManagedObjectContext *context; @property (strong, nonatomic)NSString *accessToken;  +(DataController *)sharedDataController;  -(void)generateAccessTokenWith:(NSString *)email password:(NSString *)password delegate:(id)delegate;  @end  @protocol DataControllerDelegate <NSObject>  -(void)dataFailedtoLoadWithMessage:(NSString *)message; -(void)dataFinishedLoading;  @end 
The key methods in the DataController.m file:.
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {     NSURLConnectionWithDelegate *customConnection = (NSURLConnectionWithDelegate *)connection;     NSLog(@"DidReceiveResponse from %@", customConnection.tag);     [[customConnection receivedData] setLength:0]; }  -(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {     NSURLConnectionWithDelegate *customConnection = (NSURLConnectionWithDelegate *)connection;     NSLog(@"DidReceiveData from %@", customConnection.tag);     [customConnection.receivedData appendData:data];  }  -(void)connectionDidFinishLoading:(NSURLConnection *)connection {     NSURLConnectionWithDelegate *customConnection = (NSURLConnectionWithDelegate *)connection;     NSLog(@"connectionDidFinishLoading from %@", customConnection.tag);     NSLog(@"Data: %@", customConnection.receivedData);     [customConnection.dataDelegate dataFinishedLoading]; }  -(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {     NSURLConnectionWithDelegate *customConnection = (NSURLConnectionWithDelegate *)connection;     NSLog(@"DidFailWithError with %@", customConnection.tag);     NSLog(@"Error: %@", [error localizedDescription]);     [customConnection.dataDelegate dataFailedtoLoadWithMessage:[error localizedDescription]]; } 
And to start a request: [[NSURLConnectionWithDelegate alloc] initWithRequest:request delegate:self startImmediately:YES tag:@"Login" dataDelegate:delegate];. The NSURLConnectionWithDelegate.h: @protocol DataControllerDelegate;.
@interface NSURLConnectionWithDelegate : NSURLConnection  @property (strong, nonatomic) NSString *tag; @property id <DataControllerDelegate> dataDelegate; @property (strong, nonatomic) NSMutableData *receivedData;  -(id)initWithRequest:(NSURLRequest *)request delegate:(id)delegate startImmediately:(BOOL)startImmediately tag:(NSString *)tag dataDelegate:(id)dataDelegate;  @end 
And the NSURLConnectionWithDelegate.m:.
#import "NSURLConnectionWithDelegate.h"  @implementation NSURLConnectionWithDelegate  -(id)initWithRequest:(NSURLRequest *)request delegate:(id)delegate startImmediately:(BOOL)startImmediately tag:(NSString *)tag dataDelegate:(id)dataDelegate {     self = [super initWithRequest:request delegate:delegate startImmediately:startImmediately];     if (self) {         self.tag = tag;         self.dataDelegate = dataDelegate;         self.receivedData = [[NSMutableData alloc] init];     }     return self; }  @end 

13:

Every NSURLConnection has an hash attrialthough e, you must discriminate all by this attrialthough e. . For case i need to mantain certain information before and after connection, so my RequestManager have an NSMutableDictionary to did this. . An Example:.
// Make Request NSURLRequest *request = [NSURLRequest requestWithURL:url]; NSURLConnection *c = [[NSURLConnection alloc] initWithRequest:request delegate:self];  // Append Stuffs  NSMutableDictionary *myStuff = [[NSMutableDictionary alloc] init]; [myStuff setObject:@"obj" forKey:@"key"]; NSNumber *connectionKey = [NSNumber numberWithInt:c.hash];  [connectionDatas setObject:myStuff forKey:connectionKey];  [c start]; 
After request:.
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {     NSLog(@"Received %d bytes of data",[responseData length]);      NSNumber *connectionKey = [NSNumber numberWithInt:connection.hash];      NSMutableDictionary *myStuff = [[connectionDatas objectForKey:connectionKey]mutableCopy];     [connectionDatas removeObjectForKey:connectionKey]; } 


72 out of 100 based on 67 user ratings 382 reviews

*