I'd propose a solution where you have some kind of a communication manager which has an NSOperationQueue it handles, with a single entry point method where you give it a the URL where the content lives you want to download and a success and a failure block.
The communication manager than creates an NSOperation where you create the NSURLRequest and handle the callbacks.
As soon as the commninication manager puts the operation onto the queue its start method is called.
In my communication manager implementation I keep track (besides putting the operations onto the queue) of every started operation via an NSMutableDictionary so that you can cancel a single or all operations (In the sample code provided the operationKey is used for this purpose. Here the JSONOperation returns (in case of success) an NSString to the communicator but it could be any kind of data too, e.g. I use the same class for downloading images, so I'd pass back the data object itself.
Below you can find my JSONOperation class as an example. I you like the idea I could put the other files on Gist.
My NSOperation looks like this
@interface JSONOperation : NSOperation <NSURLConnectionDataDelegate, OperationDelegate>
+ (instancetype)jsonOperationForProvider:(id)provider success:(OperationSuccessWithString)success failure:(OperationFailure)failure;
@end
#import "JSONOperation.h"
#import "ProviderDelegate.h"
@interface JSONOperation()
@property (nonatomic, assign) BOOL executing;
@property (nonatomic, assign) BOOL finished;
@property (nonatomic, assign) BOOL cancelled;
@property (nonatomic, strong) NSURL *url;
@property (nonatomic, weak) id <ProviderDelegate> delegate;
@property (nonatomic, strong) NSURLConnection *connection;
@property (nonatomic, strong) NSMutableData *receivedData;
@property (nonatomic, copy) OperationFailure failure;
@property (nonatomic, copy) OperationSuccessWithString success;
@end
@implementation JSONOperation
- (void)start
{
if ([self isCancelled])
{
[self willChangeValueForKey:@"isFinished"];
_finished = YES;
[self didChangeValueForKey:@"isFinished"];
return;
}
NSURLRequest *request = [NSURLRequest requestWithURL:self.url];
self.connection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:NO];
[self.connection scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
[self.connection start];
[self willChangeValueForKey:@"isExecuting"];
_executing = YES;
[self didChangeValueForKey:@"isExecuting"];
}
- (NSMutableData *)receivedData
{
if (nil == _receivedData) {
_receivedData = [NSMutableData data];
}
return _receivedData;
}
+ (instancetype)jsonOperationForProvider:(id <ProviderDelegate>)provider success:(OperationSuccessWithString)success failure:(OperationFailure)failure
{
NSAssert(nil != provider, @"provider parameter can't be nil");
JSONOperation *operation = [[[self class] alloc] init];
operation.delegate = provider;
operation.url = provider.contentURL;
operation.failure = failure;
operation.success = success;
return operation;
}
- (BOOL)isConcurrent {
return YES;
}
- (BOOL)isExecuting {
return _executing;
}
- (BOOL)isFinished {
return _finished;
}
- (BOOL)isCancelled {
return _cancelled;
}
#pragma mark - NSURLConnectionDataDelegate
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
if (_success) {
NSString *receivedText = [[NSString alloc] initWithData:self.receivedData encoding:NSUTF8StringEncoding];
_receivedData = nil;
self.success(self, receivedText);
}
[self willChangeValueForKey:@"isFinished"];
[self willChangeValueForKey:@"isExecuting"];
_executing = NO;
_finished = YES;
[self didChangeValueForKey:@"isExecuting"];
[self didChangeValueForKey:@"isFinished"];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[self.receivedData appendData:data];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
[connection cancel];
_connection = nil;
_receivedData = nil;
_url = nil;
if (_failure) {
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
self.failure(self, error);
}];
}
[self willChangeValueForKey:@"isFinished"];
[self willChangeValueForKey:@"isExecuting"];
_executing = NO;
_finished = YES;
[self didChangeValueForKey:@"isExecuting"];
[self didChangeValueForKey:@"isFinished"];
}
#pragma mark - OperationDelegate
- (NSString *)operationKey
{
return [self.url absoluteString];
}
- (id)provider
{
return _delegate;
}
- (void)cancelOperation
{
_failure = nil;
_success = nil;
[self.connection cancel];
_connection = nil;
_receivedData = nil;
_url = nil;
[self willChangeValueForKey:@"isCancelled"];
[self willChangeValueForKey:@"isFinished"];
[self willChangeValueForKey:@"isExecuting"];
_executing = NO;
_finished = YES;
_cancelled = YES;
[self didChangeValueForKey:@"isCancelled"];
[self didChangeValueForKey:@"isExecuting"];
[self didChangeValueForKey:@"isFinished"];
}
@end
EDIT - Gist Sample files