Ternary
Ternary

Reputation: 2421

NSOperationQueue Serialize NSURLConnection Requests

As a lot of people do, I want to perform NSURLConnections asynchronously but in series such that each requests's response doesn't step over / append onto the other's.

I have a HTTP request helper that doesn't the following:

@property (nonatomic,retain) NSURLConnection *connection;
@property (nonatomic,retain) NSOperationQueue *queue;
@property (nonatomic,retain) NSURLResponse *response;
@property (nonatomic,retain) NSMutableData *responseData;

@end

@implementation HttpRequester

@synthesize delegate = _delegate;

- (id)init {
    if(self = [super init]) {
        _connection = nil;
        // create our queue
        _queue = [[NSOperationQueue alloc] init];
        _queue.maxConcurrentOperationCount = 1; // one request at a time
        _response = nil;
        _responseData = nil;
    }
    return self;
}

- (void)sendRequest:(NSURLRequest *)request {
    _connection = [[NSURLConnection alloc] initWithRequest:request
                                                  delegate:self
                                          startImmediately:NO];
    [_connection setDelegateQueue:_queue];
    [_connection start];
}

Is it enough to create that NSOperationQueue w/ max count 1 and set that against my NSURLConnectionsuch that each call to sendRequest will queue the NSURLConnectionDelegate callbacks until the previous one has finished?

If not, I suppose the most straight forward approach would be to implement a queue around these requests I just want to understand the behavior here more.

Thanks.

Upvotes: 0

Views: 581

Answers (2)

Daij-Djan
Daij-Djan

Reputation: 50109

Since a network request is asynchronous (and you didn't use the sendSynchronousRequest helper), the operation is finishes right after starting the request and the queue moves on. If you want the queue to 'wait' for the async portion of an operation, you need to implement:

- (BOOL)isConcurrent {
    return YES;
}

then the operation queue uses KVO to monitor an operation's isFinished method.

so do:

@implementation MyOp {
     BOOL _isFinished;
} 

- (BOOL)isConcurrent {
    return YES;
}
- (BOOL)isReady {
    return YES;
}
- (BOOL)isFinished {
    return _isFinished;
}

...

THEN do what you do above and in didReceiveResponse. when you are 100% done, do

[self willChangeValueForKey:@"isFinished"];
_isFinished = YES;
[self didChangeValueForKey:@"isFinished"];

Upvotes: 1

Ternary
Ternary

Reputation: 2421

I'm also trying a simplified approach here:

@property (nonatomic,strong) NSOperationQueue *requestQueue;

@end

@implementation HttpRequester

- (id)init {
    if(self = [super init]) {
    _requestQueue = [[NSOperationQueue alloc] init];
        _requestQueue.maxConcurrentOperationCount = 1; // one request at a time
    }
    return self;
}

- (void)sendRequest:(NSURLRequest *)request {

    [NSURLConnection sendAsynchronousRequest:request queue:_requestQueue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error)
    {
        if (error != nil) {
           // error occurred
           // do something
       }
        else if (data != nil && data.length > 0) {
            // success
            // do something
        }
    }];
}

But I'm not sure if there are any advantages of one approach to another.

Upvotes: 0

Related Questions