How to create a simple NSURLRequest Callback? - ios

I have created a class that will collect data from url data asynchronously, however my understanding of callbacks or whatever is not clear and I'm trying to find a simple way to reuse my class by having the calling method wait for data to be returned or set within the ApiManager class. I just need something to wakeup in another class when that process has been completed. Some processes have single request and others have multiple, why you will notice that I'm using [connection description] within the ApiManager class.
ApiManager.h
#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>
#interface ApiManager : NSObject<NSURLConnectionDelegate>
{
NSMutableDictionary *_dataDictionary;
}
- (void)urlRequest:(NSURLRequest *)url;
#property (strong, nonatomic) NSMutableArray *results;
#end
ApiManager.m
#import "ApiManager.h"
#implementation ApiManager
- (void)urlRequest:(NSURLRequest *)url {
[[NSURLConnection alloc] initWithRequest:url delegate:self];
}
// basic connection classes
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
NSMutableData *responceOjb = _dataDictionary[ [connection description] ];
[_dataDictionary setObject:responceOjb forKey:[connection description]];
}
// append any data we find
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
NSMutableData *responceOjb = _dataDictionary[ [connection description] ];
[responceOjb appendData: data];
[_dataDictionary setObject:responceOjb forKey:[connection description]];
}
// --
- (NSCachedURLResponse *)connection:(NSURLConnection *)connection
willCacheResponse:(NSCachedURLResponse*)cachedResponse {
// Return nil to indicate not necessary to store a cached response for this connection
return nil;
}
// wrap up and close the connect, move objects over to results or something
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
[_results addObject:[connection description]];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
// The request has failed for some reason!
// Check the error var
NSLog(#"%#",error);
}
#end
The main View Controller test:
#import "ViewController.h"
#import "ApiManager.h"
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[self DoThisTest];
}
-(void)DoThisTest {
ApiManager *api = [[ApiManager alloc] init];
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:[NSString stringWithFormat:#"%#",#"http://google.com"]]];
[api urlRequest:request];
if([api results]) {
NSLog(#"GOT DATA");
}
}

Well, there are a few options. You could add a block property onto your ApiManager class:
#property (copy, nonatomic) void (^doneHandler)();
And then invoke that block like so:
self.doneHandler();
You would invoke the block when you deem it appropriate (say, in your connectionDidFinishLoading: method).
With this approach, the definition of the block (callback) would happen in your view controller and look something like:
ApiManager *apiManager = [[ApiManager alloc] init];
apiManager.doneHandler = ^{
// Do whatever you need to do here.
};
Alternatively, you could add a method to your ApiManager with a signature like this:
- (void)sendRequestWithURL:(NSURL*)url completion:(void(^)())completion;
And use NSURLConnection's (or, better, NSURLSession's) block-based APIs. Those APIs have callbacks built in and you would simply invoke completion(); inside of the completion block of -[NSURLSession sendAsynchronousRequest:completion:].
Finally, you could define an ApiManagerDelegate protocol.
- (void)apiManagerDidFinishReceivingData:(ApiManager*)sender;
And add a delegate property to your ApiManager class.
#property (weak, nonatomic) id<ApiManagerDelegate>delegate;
Assign the delegate of your ApiManager in your ViewController:
ApiManager *apiManager = [[ApiManager alloc] init];
apiManager.delegate = self;
Call the delegate method inside of your implementation of NSURLConnectionDelegate's callbacks in ApiManager like so:
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
[_results addObject:[connection description]];
[self.delegate apiManagerDidFinishReceivingData:self];
}
And implement the delegate method in ViewController:
- (void)apiManagerDidFinishReceivingData:(ApiManager*)sender {
// Do what you want to.
}
As an addendum, there are networking libraries available that do a lot of the heavy lifting and busy-work for you, most notably AFNetworking, if you're just trying to get stuff done. And, even if this is more of an academic exercise where you're trying to understand the patterns, looking at AFNetworking's APIs and implementation (it's open source) would be highly instructive.
Cheers

Related

iOS: How to trap all NSURLRequests and NSURLResponses?

I want to log all the NSURLRequests which are initiated from within my app and response for those requests. I wrote a custom NSURLProtocol to achieve this. I was able to trap all the request but not the response.
In canInitWithRequest method I can log all the requests regardless of method returns YES/NO.
Right now I am returning YES in hope the of actual response.
In -startLoading method I am supposed to inform NSURLProtocolClient with response and progress. I am not interested in modifying/creating my own response instead I am interested in actual response and want to log it. When and where would I find actual response for the request?
I am not interested in modifying URL loading behavior.
Am I on the right track with custom protocol or is there something else I need to do to log all the requests and responses?
#implementation TestURLProtocol
+(BOOL)canInitWithRequest:(NSURLRequest *)request
{
//here i can log all the requests
NSLog(#"TestURLProtocol : canInitWithRequest");
return YES;
}
+(NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request
{
return request;
}
-(void)startLoading
{
// I don't want to create or modify response.
//I have nothing todo with response instead I need actual response for logging purpose.
NSURLResponse * response = [[NSURLResponse alloc] initWithURL:[self.request URL] MIMEType:#"text/html" expectedContentLength:-1 textEncodingName:#"utf8"];
[[self client] URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
[[self client] URLProtocolDidFinishLoading:self];
}
-(void)stopLoading
{
}
#end
Well, I dropped the idea of custom NSURLProtocol. I wrote a class which confirms to NSURLConnectionDelegate, NSURLConnectionDataDelegate etc as per your need. This class serves as a common delegate to all NSURLConnection instances. It has a property of type id(depend upon requirements), this property holds the object which is creating NSURLConnection and interested in delegate callbacks as well.
URLRequestLogger instance serves as a common delegate to all NSURLConnection instances.
Each NSURLConnection has a URLRequestLogger instance as a delegate.
#import <Foundation/Foundation.h>
#interface URLRequestLogger : NSObject<NSURLConnectionDataDelegate>
-(id)initWithConnectionDelegate:(id<NSURLConnectionDataDelegate>)connectionDelegate;
#end
implementation file
#import "URLRequestLogger.h"
#interface URLRequestLogger ()
#property(nonatomic, assign) id<NSURLConnectionDataDelegate> connectionDelegate;
#end
#implementation URLRequestLogger
-(id)initWithConnectionDelegate:(id<NSURLConnectionDataDelegate>)connectionDelegate
{
self = [super init];
if (self)
{
_connectionDelegate = connectionDelegate;
}
return self;
}
//one can implement all the delegates related to NSURLConnection as per need
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
//log reponse info
if ([response isKindOfClass:[NSHTTPURLResponse class]])
{
NSHTTPURLResponse * httpResponse = (NSHTTPURLResponse *)response;
NSDictionary * responseHeaders = [httpResponse allHeaderFields];
NSInteger statusCode = [httpResponse statusCode];
//save as many info as we can
}
//if connectionDelegate is interested in it, inform it
if ([self.connectionDelegate respondsToSelector:#selector(connection:didReceiveResponse:)])
{
[self.connectionDelegate connection:connection didReceiveResponse:response];
}
}
-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
//log the error and other info
//if connectionDelegate is interested in it, inform it
if ([self.connectionDelegate respondsToSelector:#selector(connection:didFailWithError:)])
{
[self.connectionDelegate connection:connection didFailWithError:error];
}
}
-(void)connectionDidFinishLoading:(NSURLConnection *)connection
{
//log the connection finish info
//response time and download time etc
//if connectionDelegate is interested in it, inform it
if ([self.connectionDelegate respondsToSelector:#selector(connectionDidFinishLoading:)])
{
[self.connectionDelegate connectionDidFinishLoading:connection];
}
}
#end
MyViewController creates an NSURLConnection and is interested in delegate methods as well. At the same time we want to log all the details about request and response.
#import <UIKit/UIKit.h>
#interface MyViewController : UIViewController<NSURLConnectionDataDelegate>
#end
implementation file
#import "MyViewController.h"
#import "URLRequestLogger.h"
#implementation MyViewController
//MyViewController class creates a request and interested in connection delegate callbacks
//MyViewController confirms to NSURLConnectionDelegate.
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
[self sendRequest];
}
-(void)sendRequest
{
/*
connection delegate here would be our URLRequestLogger class instance which holds MyViewController instance
to return the callbacks here after logging operations are finished
*/
URLRequestLogger * requestLogger = [[URLRequestLogger alloc] initWithConnectionDelegate:self];
[NSURLConnection connectionWithRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:#"http://www.example.org"]] delegate:requestLogger];
}
#pragma mark - NSURLConnection delegate methods
-(void)connectionDidFinishLoading:(NSURLConnection *)connection
{
//this callback is received from URLRequestLogger after logging operation is completed
//do something. Update UI etc...
}
-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
//this callback is received from URLRequestLogger after logging operation is completed
//do something. Update UI etc...
}
#end
Better solution with custom NSURLProtocol:
One can log all outgoing URL request and alter request properties, response and do other things like loading encrypted html files from disk into webview etc.
.h file
#import <Foundation/Foundation.h>
#interface URLRequestLoggerProtocol : NSURLProtocol
#end
.m file
#import "URLRequestLoggerProtocol.h"
#interface URLRequestLoggerProtocol ()<NSURLConnectionDelegate,NSURLConnectionDataDelegate>
#property(nonatomic, strong) NSURLConnection * connection;
#end
#implementation URLRequestLoggerProtocol
#synthesize connection;
+(BOOL)canInitWithRequest:(NSURLRequest *)request
{
//logging only HTTP and HTTPS requests which are not being logged
NSString * urlScheme = request.URL.scheme;
if (([urlScheme isEqualToString:#"http"] || [urlScheme isEqualToString:#"https"]) && ![[NSURLProtocol propertyForKey:#"isBeingLogged" inRequest:request] boolValue])
{
return YES;
}
return NO;
}
+(NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request
{
return request;
}
-(void)startLoading
{
NSMutableURLRequest * newRequest = [self.request mutableCopy];
[NSURLProtocol setProperty:[NSNumber numberWithBool:YES] forKey:#"isBeingLogged" inRequest:newRequest];
self.connection = [NSURLConnection connectionWithRequest:newRequest delegate:self];
}
-(void)stopLoading
{
[self.connection cancel];
}
#pragma mark - NSURLConnectionDelegate methods
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
//log all things along with error and finally inform URL client that you are done
[self.client URLProtocol:self didFailWithError:error];
}
#pragma mark - NSURLConnectionDataDelegate methods
-(NSURLRequest *)connection:(NSURLConnection *)connection willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)response
{
//start logging a request properties from here
return request;
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
//log the response and other things and inform client that you are done.
[self.client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageAllowed];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
//log the data and other things and inform client that you are done.
[self.client URLProtocol:self didLoadData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
//log the finish info and other things and inform client that you are done.
[self.client URLProtocolDidFinishLoading:self];
}
#end

nsdata that are return fron NSObject class and used in viewcontroller class

I have an NSObject class that contains 3 methods:
-(void)RequestForData
{
#pragma Mark - ASIHTTPRequest
NSURL *url=[NSURL URLWithString:#"http://srv2.vitaminas.it/pdv"];
ASIHTTPRequest *request=[ASIHTTPRequest requestWithURL:url];
[request setDelegate:self];
[request startSynchronous];
}
pragma Mark - HTTP Delegate
- (NSData*)requestFinished:(ASIHTTPRequest *)request
{
NSData *responseData = [request responseData];
return responseData;
}
- (void)requestFailed:(ASIHTTPRequest *)request
{
NSError *error = [request error];
NSLog(#"%#",error);
}
I have one view controller class, from viewDidLoad method of viewcontroller class call -RequestForData method of NSObject class,
WebServiceMethods *web=[[WebServiceMethods alloc]init];
[web RequestForData];
arr_JsonData=[NSJSONSerialization JSONObjectWithData:web options:NSUTF8StringEncoding error:Nil];
NSLog(#"%#",arr_JsonData);
[self.tableView reloadData];
But I want to use NSData that are returned from NSObject class (i.e return responsedata; ) into view controller class.
I want that NSData into arr_JsonData ( NSMutuableArray )
What can I do ?
Make responseData as class level variable. Do not create local instance of it in requestFinished method.
Your problem is callbacks. You should put your viewController as a delegate of WebServiceMethods ( or using blocks is better) to be informed when the request has finished and the populate your arr_JsonData
#protocol WebServiceMethodsDelegate
- (void)webServiceMethodsDidFinishWithSucess:(NSString *)response; // give this méthode an appropriate name.
- (void)webServiceMethodsDidFailWithError:(NSError *)error;
#end
#interface WebServiceMethods : NSObject
#property (nonatomic,weak) id <WebServiceMethodsDelegate> delegate;
#end
#implemntation WebServiceMethods : NSObject
- (void)requestFinished:(ASIHTTPRequest *)request
{
NSString *responseString = [request responseString];
[self.delegate webServiceMethodsDidFinishWithSucess:responseString];
}
- (void)requestFailed:(ASIHTTPRequest *)request
{
NSError *error = [request error];
[self.delegate webServiceMethodsDidFailWithError:error];
}
#end
Put your viewController conform to the WebServiceMethodsDelegate protocol.
#interface yourViewController : UIViewController <WebServiceMethodsDelegate>
...
#end
and know in your the viewDidLoad of your viewController :
WebServiceMethods *web=[[WebServiceMethods alloc]init];
web.delegate = self;
[web RequestForData];
Put also the delegate methods in viewController.m
- (void)webServiceMethodsDidFinishWithSucess:(NSString *)response {
// here you can parse the response and reload your tableView
.....
[self.tableView reloadData];
}
- (void)webServiceMethodsDidFailWithError:(NSError *)error {
// handle the errors
}
PS : There are many problems with your code :
Don't use ASHTTPRequest, it's not maintained. You can use AFNetworking.AFNetworking
Put your WebServiceMethods as a shared instance.
....

NSURLConnection Data Retrieval

I'm having a very puzzling issue with NSURLConnection. It is returning the data in another thread or something, things are not working in the proper order. I'm supposed to receive the json data then parse it, but the data is actually being received after the parser fails. Here is the log.
2013-09-22 14:44:40.454 WebServiceExample[39306:a0b] Parsing Error: Error Domain=NSCocoaErrorDomain Code=3840 "The operation couldn’t be completed. (Cocoa error 3840.)" (No value.) UserInfo=0xa0b47e0 {NSDebugDescription=No value.}
2013-09-22 14:44:41.312 WebServiceExample[39306:a0b] Succeeded! Received 112 bytes of data
I'm making a small framework to ease my life to connect to rails API, I'm calling it objective rails OR, but I already spent the entire weekend trying to figure out what I'm doing wrong. Here's the code:
#interface ORObject : NSObject
#property (nonatomic, retain) NSMutableDictionary *properties;
#property (nonatomic, retain) ORWebManager *webManager;
#property (nonatomic, retain) NSString *route;
#property (nonatomic, retain) NSString *singularClassName;
- (id) initWithRoute:(NSString *) route webManager:(ORWebManager *) webManager;
- (id) initWithRoute:(NSString *) route;
- (ORObject *) find:(NSInteger) identifier;
#end
Here is the implementation of find:
- (ORObject *) find:(NSInteger) identifier
{
NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:#"%#/%#/%ld.%#", [ ORConfig sharedInstance].baseURL, self.route, (long)identifier, self.webManager. parser.contentType]];
ORObject *object = [self.webManager httpGet:url];
if (object) {
object.route = self.route;
object.webManager = self.webManager;
}
return object;
}
Here is the httpGet method of the ORWebManager class
- (ORObject *) httpGet:(NSURL *)url
{
ORGetRequester *requester = [[ORGetRequester alloc] init];
NSMutableData *data = [requester request:url];
return [self.parser toObject:data];
}
The superclass of ORGetRequester is ORHTTPRequester and it implements the NSURLConnectionDelegate protocol methods
#interface ORHTTPRequester : NSObject<NSURLConnectionDelegate>
#property (nonatomic, retain) NSMutableData *receivedData;
#property (nonatomic, retain) NSMutableURLRequest *req;
#property (nonatomic, retain) NSURLConnection *connection;
#end
#implementation ORHTTPRequester
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse
[self.receivedData setLength:0];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
[self.receivedData appendData:data];
}
- (void)connection:(NSURLConnection *)connection
didFailWithError:(NSError *)error
{
NSLog(#"Connection failed! Error - %# %#",
[error localizedDescription],
[[error userInfo] objectForKey:NSURLErrorFailingURLStringErrorKey]);
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
NSLog(#"Succeeded! Received %d bytes of data",[self.receivedData length]);
}
#end
And finally here is ORGetRequester, this is where the "magic" happens.
#interface ORGetRequester : ORHTTPRequester
- (NSMutableData *) request:(NSURL *)url;
#end
#implementation ORGetRequester
- (NSMutableData *) request:(NSURL *)url
{
self.req = [NSMutableURLRequest requestWithURL:url
cachePolicy:NSURLRequestUseProtocolCachePol icy
timeoutInterval:60.0];
self.receivedData = [[NSMutableData alloc] init];
self.connection = [[NSURLConnection alloc] initWithRequest:self.req delegate:self];
if (!self.connection) {
NSLog(#"Connection Failed");
}
return self.receivedData;
}
#end
self.connection = [[NSURLConnection alloc] initWithRequest:self.req delegate:self];
returns immediately because it starts an async operation. You need to parse the data when connectionDidFinishLoading: is called.
Or consider using:
+ (void)sendAsynchronousRequest:(NSURLRequest *)request queue:(NSOperationQueue *)queue completionHandler:(void (^)(NSURLResponse*, NSData*, NSError*))handler
The completion block will be called when the http operations is complete. Process the data in the completion block and notify any object that needs the data. This is probably a good bet if you do not need the other delegate methods.

Subclassing NSURLConnection for authentication?

I have a subclass:
#import <Foundation/Foundation.h>
#interface CustomURLConnection : NSURLConnection <NSURLConnectionDelegate>
#end
In it's implementation file i have the following function:
-(void) connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
{
NSLog(#"Authenticating from subclass.");
}
note that didReceiveAuthenticationChallenge is part of NSURLConnectionDelegate
This snippet is currently in every class that sends a NSURLRequest using NSURLConnection:
-(void) connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
{
// Code to authenticate ourselves.
}
The actual problem:
I want the subclass to have a predetermined behaviour for
connection:(CustomURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
instead of having to implement the function in every class. And instead have every class use the subclass instead and have every authentication challenge handled automatically by the subclass.
The class is allocated as follows:
cUrlConnection = [[CustomURLConnection alloc]initWithRequest:req delegate:self startImmediately: YES];
if (cUrlConnection)
{
// Handle events when connection is active.
}
If anyone has any insight in how i make my CustomURLConnection handle the authentication mechanism and/or tips/pointers i'd be delighted.
It's a somewhat awkward pattern, but what you want to do in the subclass is take over the delegate setting/getting and the provide a different, private-implementation delegate to the superclass that implements this method the way you want and passes all the other methods to the user-supplied delegate. (Note: since NSURLConnection takes its delegate at init-time, there's no -delegate and -setDelegate: methods to override. This is untested, but it might look something like this:
#interface MyCustomURLConnection : NSURLConnection
#end
#interface MyPrivateImpDelegate : NSObject
- (id)initWithRealDelegate: (id<NSURLConnectionDelegate>)realDel;
#end
#implementation MyPrivateImpDelegate
{
__weak id<NSURLConnectionDelegate> _userDelegate;
}
- (id)initWithRealDelegate:(id<NSURLConnectionDelegate>)realDel
{
if (self = [super init])
{
_userDelegate = realDel;
}
return self;
}
- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
{
// Your auth implementation here...
// maybe call through if you want?
if ([_userDelegate respondsToSelector: _cmd])
{
[_userDelegate connection:connection didReceiveAuthenticationChallenge:challenge];
}
}
- (id)forwardingTargetForSelector:(SEL)aSelector
{
return _userDelegate;
}
- (BOOL)respondsToSelector:(SEL)aSelector
{
BOOL retVal = [super respondsToSelector: aSelector];
if (!retVal)
{
retVal = [_userDelegate respondsToSelector: aSelector];
}
return retVal;
}
#end
#implementation MyCustomURLConnection
{
MyPrivateImpDelegate* _fakeDelegate;
}
- (id)initWithRequest:(NSURLRequest *)request delegate:(id)delegate startImmediately:(BOOL)startImmediately
{
MyPrivateImpDelegate* privDel = [[MyPrivateImpDelegate alloc] initWithRealDelegate: delegate];
if (self = [super initWithRequest:request delegate:privDel startImmediately:startImmediately])
{
_fakeDelegate = privDel; // make sure our private delegate lives as long as we do.
}
return self;
}
- (void)dealloc
{
_fakeDelegate = nil;
}
#end

How to make sure I have only 1 asynchronous NSURLConnection at a time?

I'm creating an app which communicates alot with a server. I want to make sure I have only 1 connection at a time - only 1 request pending. I want so that if I try to send another request, It will wait until the current one is finished before sending the next.
How can I implement This?
Tnx!
I don't know of any automatic mechanism to do this. So you have to write a new class yourself (let's call it ConnectionQueue):
Basically, instead of a creating the NSURLConnection directly, you call a method of your ConnectionQueue class (which should have exactly one instance) taking the NSURLRequest and the delegate as a parameter. Both are added to a queue, i.e. a separate NSArray for the requests and the delegates.
If the arrays contains just one element, then no request is outstanding and you can create a NSURLConnection with the specified request. However, instead of the delegate passed to the method, you pass your ConnectionQueue instance as the delegate. As a result, the connection queue will be informed about all actions of the connection. In most cases, you just forward the callback to the original delegate (you'll find it in the first element of the delegate array).
However, if the outstanding connection completes (connection:didFailWithError: or connectionDidFinishLoading: is called), you first call the original delegate and then remove the connection from the two arrays. Finally, if the arrays aren't empty, you start the next connection.
Update:
Here's some code. It compiles but hasn't been tested otherwise. Furthermore, the implementation of the NSURLConnectionDelegate protocol is incomplete. If you're expecting more than implemented callbacks, you'll have to add them.
Header File:
#import <Foundation/Foundation.h>
#interface ConnectionQueue : NSObject {
NSMutableArray *requestQueue;
NSMutableArray *delegateQueue;
NSURLConnection *currentConnection;
}
// Singleton instance
+ (ConnectionQueue *)sharedInstance;
// Cleanup and release queue
+ (void)releaseShared;
// Queue a new connection
- (void)queueRequest:(NSURLRequest *)request delegate:(id)delegate;
#end
Implementation:
#import "ConnectionQueue.h"
#implementation ConnectionQueue
static ConnectionQueue *sharedInstance = nil;
+ (ConnectionQueue*)sharedInstance
{
if (sharedInstance == nil)
sharedInstance = [[ConnectionQueue alloc] init];
return sharedInstance;
}
+ (void)releaseShared
{
[sharedInstance release];
sharedInstance = nil;
}
- (id)init
{
if ((self = [super init])) {
requestQueue = [NSMutableArray arrayWithCapacity:8];
delegateQueue = [NSMutableArray arrayWithCapacity:8];
}
return self;
}
- (void)dealloc
{
[requestQueue release];
[delegateQueue release];
[currentConnection cancel];
[currentConnection release];
[super dealloc];
}
- (void)startNextConnection
{
NSURLRequest *request = [requestQueue objectAtIndex:0];
currentConnection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
}
- (void)queueRequest:(NSURLRequest *)request delegate:(id)delegate
{
[requestQueue addObject:request];
[delegateQueue addObject:delegate];
if ([requestQueue count] == 1)
[self startNextConnection];
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
id delegate = [delegateQueue objectAtIndex:0];
[delegate connection: connection didReceiveResponse: response];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
id delegate = [delegateQueue objectAtIndex:0];
[delegate connection: connection didReceiveData: data];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
id delegate = [delegateQueue objectAtIndex:0];
[delegate connection: connection didFailWithError:error];
[currentConnection release];
currentConnection = nil;
[requestQueue removeObjectAtIndex:0];
[delegateQueue removeObjectAtIndex:0];
if ([requestQueue count] >= 1)
[self startNextConnection];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
id delegate = [delegateQueue objectAtIndex:0];
[delegate connectionDidFinishLoading: connection];
[currentConnection release];
currentConnection = nil;
[requestQueue removeObjectAtIndex:0];
[delegateQueue removeObjectAtIndex:0];
if ([requestQueue count] >= 1)
[self startNextConnection];
}
#end
To use the connection queue, create an NSURLRequest instance and then call:
[[ConnectionQueue sharedInstance] queueRequest:request delegate:self];
There's no need to create the singleton instance of ConnectionQueue explicitly. It will be created automatically. However, to properly clean up, you should call [ConnectionQueue releaseShared] when the application quits, e.g. from applicationWillTerminate: of your application delegate.

Resources