How to batch request with AFNetworking 2?

Thanks Sendoa for the link to the GitHub issue where Mattt explains why this functionality is not working anymore. There is a clear reason why this isn't possible with the new NSURLSession structure; Tasks just aren't operations, so the old way of using dependencies or batches of operations won't work.

I've created this solution using a dispatch_group that makes it possible to batch requests using NSURLSession, here is the (pseudo-)code:

// Create a dispatch group
dispatch_group_t group = dispatch_group_create();

for (int i = 0; i < 10; i++) {
    // Enter the group for each request we create
    dispatch_group_enter(group);

    // Fire the request
    [self GET:@"endpoint.json"
       parameters:nil
          success:^(NSURLSessionDataTask *task, id responseObject) {
                  // Leave the group as soon as the request succeeded
                  dispatch_group_leave(group);
          }
      failure:^(NSURLSessionDataTask *task, NSError *error) {
                  // Leave the group as soon as the request failed
                  dispatch_group_leave(group);
              }];
}

// Here we wait for all the requests to finish
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
    // Do whatever you need to do when all requests are finished
});

I want to look write something that makes this easier to do and discuss with Matt if this is something (when implemented nicely) that could be merged into AFNetworking. In my opinion it would be great to do something like this with the library itself. But I have to check when I have some spare time for that.


Just updating the thread... I had the same problem and after some researches I found some good solutions, but I decided to stick with this one:

I am using the project called Bolts. So, for the same sample above posted by @Mac_Cain13, it would be:

[[BFTask taskWithResult:nil] continueWithBlock:^id(BFTask *task) {
    BFTask *task = [BFTask taskWithResult:nil];
    for (int i = 0; i < 10; i++) {
        task = [task continueWithBlock:^id(BFTask *task) {
            return [self executeEndPointAsync];
        }];
    }
    return task;
}] continueWithBlock:^id(BFTask *task) {
    // Everything was executed.
    return nil;
}];;

- (BFTask *) executeEndPointAsync {
    BFTaskCompletionSource *task = [BFTaskCompletionSource taskCompletionSource];
    [self GET:@"endpoint.json" parameters:nil
      success:^(NSURLSessionDataTask *task, id responseObject) {
        [task setResult:responseObject];
      }
      failure:^(NSURLSessionDataTask *task, NSError *error) {
        [task setError:error];
      }];
    }];
    return task.task;
}

Basically, it's stacking all of the tasks, waiting and unwrapping until there is no more tasks, and after everything is completed the last completion block is executed.

Another project that does the same thing is RXPromise, but for me the code in Bolts was more clear.


For request which can be post or get, you can use AFNetworking 2.0 for batch operation as firstly you need to create operation like this:

//Request 1
NSString *strURL = [NSString stringWithFormat:@"your url here"];
NSLog(@"scheduleurl : %@",strURL);
NSDictionary *dictParameters = your parameters here
NSMutableURLRequest *request = [[AFHTTPRequestSerializer serializer] requestWithMethod:@"POST" URLString:strURL parameters:dictParameters error: nil];

AFHTTPRequestOperation *operationOne = [[AFHTTPRequestOperation alloc] initWithRequest:request];
operationOne = [AFHTTPResponseSerializer serializer];

[operationOne setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject)
{
     //do something on completion
} 
failure:^(AFHTTPRequestOperation *operation, NSError *error)
{
     NSLog(@"%@",[error description]);
}];

//Request 2
NSString *strURL1 = [NSString stringWithFormat:@"your url here"];
NSLog(@"scheduleurl : %@",strURL);
NSDictionary *dictParameters1 = your parameters here
NSMutableURLRequest *request1 = [[AFHTTPRequestSerializer serializer] requestWithMethod:@"POST" URLString:strURL1 parameters:dictParameters1 error: nil];

AFHTTPRequestOperation *operationTwo = [[AFHTTPRequestOperation alloc] initWithRequest:request1];
operationTwo = [AFHTTPResponseSerializer serializer];

[operationTwo setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject)
{
     //do something on completion
} 
failure:^(AFHTTPRequestOperation *operation, NSError *error)
{
     NSLog(@"%@",[error description]);
}];

//Request more here if any

Now perform batch operation like this :

//Batch operation
//Add all operation here 
NSArray *operations = [AFURLConnectionOperation batchOfRequestOperations:@[operationOne,operationTwo] progressBlock:^(NSUInteger numberOfFinishedOperations, NSUInteger totalNumberOfOperations)
{
    NSLog(@"%i of %i complete",numberOfFinishedOperations,totalNumberOfOperations);
    //set progress here
    yourProgressView.progress = (float)numberOfFinishedOperations/(float)totalNumberOfOperations;

} completionBlock:^(NSArray *operations) 
{
    NSLog(@"All operations in batch complete");
}];

[[NSOperationQueue mainQueue] addOperations:operations waitUntilFinished:NO];

On AFNetworking 2.0, AFHTTPClient has been split on AFHTTPRequestOperationManager and AFHTTPSessionManager, so probably you could start with the first, which has operationQueue property.