1

I have a method that downloads a list of files from a web server to an iPad using the AFNetworking call enqueueBatchOfHTTPRequestOperations. Every once in a while I get a failed operation (usually when the file is larger). Historically, I downloaded each file individually and simply called the operation again up to a certain retry count.

I've refactored the code to use enqueueBatchOfHTTPRequestOperations. This works great but I don't know how to get notifications on "specific" operation failures and I don't know how to re-add them to the queue if they have failed.

Here's the code I'm using to download "n" numbers of files:

    NSMutableArray *operationsArray = [[NSMutableArray alloc]init];
for (MINPageDefinition *currPage in [[MINPageStore sharedInstance] pageArray])
{
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    NSString *serverLibraryURL = [defaults objectForKey:kRootURL];
    serverLibraryURL = [serverLibraryURL stringByAppendingPathComponent:kPageDefinitionsDirectory];
    serverLibraryURL = [serverLibraryURL stringByAppendingPathComponent:currPage.pageImageName];

    NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:serverLibraryURL]];
    AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
    operation.outputStream = [NSOutputStream outputStreamToFileAtPath:currPage.pageImageURL append:NO];

    [operationsArray addObject:operation];
}
if ([operationsArray count] > 0)
{
    AFHTTPClient *client = [[AFHTTPClient alloc] initWithBaseURL:[NSURL URLWithString:@""]];
    void (^progressBlock)(NSUInteger numberOfCompletedOperations, NSUInteger totalNumberOfOperations) = ^(NSUInteger numberOfCompletedOperations, NSUInteger totalNumberOfOperations) {
        NSLog(@"%d proccesses completed out of %d", numberOfCompletedOperations, totalNumberOfOperations);
    };
    void (^completionBlock)(NSArray *operations) = ^(NSArray *operations) {
        NSLog(@"All operations completed");
    };
    [client enqueueBatchOfHTTPRequestOperations:operationsArray
                                  progressBlock:progressBlock
                                completionBlock:completionBlock];
}

Before, I had a method that executed the operation. On failure, the failure block would call itself recursively. How can I modify this code to retry up to "n" numbers of times if an operation fails?

JustLearningAgain
  • 2,227
  • 4
  • 34
  • 50
  • pretty close ... http://stackoverflow.com/questions/12220986/afnetworking-how-to-setup-requests-to-be-retried-in-the-event-of-a-timeout – TonyMkenu Jan 08 '13 at 09:13
  • you can also check https://github.com/AFNetworking/AFNetworking/issues/596 – TonyMkenu Jan 08 '13 at 09:22
  • 1
    Actually, in my original implementation, this was exactly what I was doing. If an operation failed, I recursively called the method that encapsulated the operation. The problem I've having now is because i've changed the implementation over to queueing operations using enqueueBatchOfHTTPRequestOperations. This method iterates through "n" numbers of operations. I know that I can iterate through the operations in the handler. But how do I re-add and then re-queue those operations? – JustLearningAgain Jan 08 '13 at 15:30

3 Answers3

1

Check Below Code @justLearningAgain

[[WfServices sharedClient] GET:kGetAddress parameters:dicParam  success:^(AFHTTPRequestOperation *operation, id responseObject) {
        NSLog(@"RESPONSE ::: %@",responseObject);
  }failure:^(AFHTTPRequestOperation *operation, NSError *error) {
    }];


+ (WfServices *)sharedClient
{
    static WfServices * _sharedClient = nil;
    static dispatch_once_t onceToken;

    dispatch_once(&onceToken, ^{
        _sharedClient = [[WfServices alloc] initWithBaseURL:[NSURL URLWithString:kWFBaseURLString]];
    });

    return _sharedClient;
}
mshau
  • 503
  • 4
  • 19
0

Can't you just enqueue the single failed operation again in that operation's failure block like so?

[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {

} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
     [[APIClient sharedClient] enqueueHTTPRequestOperation:operation];
}];

Where [APIClient sharedClient] is a reference to your AFHTTPClient subclass (singleton or not).

Stunner
  • 12,025
  • 12
  • 86
  • 145
Alfie Hanssen
  • 16,964
  • 12
  • 68
  • 74
  • No, because you can't add a finished operation to an operation queue. It will throw an `NSInvalidArgumentException` if you try. Even though the request failed at the network level, the operation itself completed. – Steve Madsen Feb 12 '14 at 22:33
0

I think AFNetworking Auto-Retry might be helpful.

Or you can override following method in your AFHTTPClient.

Consider resumeTasksArray = array of operations which you need to re add to the queue.

NSMutableArray *resumeTasksArray; before @implementation AFHTTPClient 

- (AFHTTPRequestOperation *)HTTPRequestOperationWithRequest:(NSURLRequest *)request
                                                success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success
                                                failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure {

void(^authFailBlock)(AFHTTPRequestOperation *opr, NSError *err) = ^(AFHTTPRequestOperation *opr, NSError *err){
    [resumeTasksArray addObject:request];
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
        //now, queue up and execute the original task
        for (NSURLRequest *previousRequest in resumeTasksArray) {
            NSLog(@"^^^^^^^^^^^^^^^^^^^^^^^^^^^^Resume Task URL - %@", previousRequest.URL);
            NSMutableURLRequest *newRequest = [previousRequest mutableCopy];
            [newRequest setValue:[NSString stringWithFormat:@"Bearer %@", AppSingleton.access_token] forHTTPHeaderField:@"Authorization"];
            AFHTTPRequestOperation *opera10n = [[AFHTTPRequestOperation alloc]initWithRequest:newRequest];
            opera10n.responseSerializer = self.responseSerializer;
            [opera10n setCompletionBlockWithSuccess:success failure:failure];
            if (![[self.operationQueue operations] containsObject:opera10n]) {
                [[self operationQueue] addOperation:opera10n];
                [opera10n start];
            }
        }
        [resumeTasksArray removeAllObjects];
    });
};
AFHTTPRequestOperation *operation = [super HTTPRequestOperationWithRequest:request success:success failure:authFailBlock];
return operation;
}
Pratik Mistry
  • 2,905
  • 1
  • 22
  • 35