1


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!

Niv
  • 2,294
  • 5
  • 29
  • 41

1 Answers1

1

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.

Codo
  • 75,595
  • 17
  • 168
  • 206
  • I've managed to implement this by now, and it's pertty much what I've written. Just some wondering/comments:1.is there a difference between keeping 2 arrays and keeping 1 array of dictionaries? 2.shouldn't you check if the delegate responds to the messages before you send them? I believe that's the expected functionality from NSURLConnection 3.a bit general and not specific to this code, but how does the sharedInstance methods work? who is responsible to keep the sharedInstance, under what block does it exist? – Niv Aug 07 '11 at 09:39
  • 1. Using two arrays is more pragmatic and uses less memory. Dictionaries are ugly in this case. If you want a clean solution with just one array, implement a specific class holding the request and the delegate. 2. Yes, I should have checked whether the delegate responds to the message. An alternative would be to implement a class to intercepts the two relevant messages and forwards all the remaining ones as in http://stackoverflow.com/questions/3498158/intercept-obj-c-delegate-messages-within-a-subclass/3862591#3862591. 3. See my update at the bottom of the answer. – Codo Aug 07 '11 at 10:25