How do I make multiple GET requests and wait for all operations to complete?

For my application, I need to load different data from the server, so I need to make a couple of GET

requests. For every GET request, I need to store the data in my CoreData ... I need a callback or success block for each operation.

I tried a couple of things, but I couldn't get it to work.

When I make one request GET

:

    AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
    [manager GET:[NSString stringWithFormat:@"%@/api/actors", [NSString stringWithUTF8String:kBaseURL]] parameters:nil 
success:^(AFHTTPRequestOperation *operation, id responseObject) {
  // Core Data saving
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
    NSLog(@"Error: %@", error);
}];

      

How do I make several GET

and wait for all my requests to complete?

If someone writes me a solution ... Does this work for other types of queries? (POST, DELETE, PUT ... etc.)

thank

+3


source to share


6 answers


I found a good solution with AFNetworking

and I could do exactly what I wanted:

NSArray *operations = [AFURLConnectionOperation batchOfRequestOperations:mutableOperations progressBlock:^(NSUInteger numberOfFinishedOperations, NSUInteger totalNumberOfOperations) {
        NSLog(@"%lu of %lu complete", numberOfFinishedOperations, totalNumberOfOperations);
    } completionBlock:^(NSArray *operations) {
        [self.delegate dataRefreshingDone];
    }];
    [[NSOperationQueue mainQueue] addOperations:operations waitUntilFinished:NO];

      

He will perform all my operations GET

one at a time. I created a custom one getter

to create an operation according to url

.



- (AFHTTPRequestOperation *)operationGetForUrl:(NSString *)url
{
    NSURL *URL = [NSURL URLWithString:[NSString stringWithFormat:@"%@/api/%@", [NSString stringWithUTF8String:kBaseURL], url]];
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:URL];
    [request setHTTPMethod:@"GET"];
    AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
    operation.responseSerializer = [AFJSONResponseSerializer serializer];
    [operation.responseSerializer setAcceptableContentTypes:    [operation.responseSerializer.acceptableContentTypes setByAddingObject:@"text/html"]];
    [operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
       // YOUR SUCCESS BLOCK
    } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
       // FAILURE BLOCK
    }];
    return operation;
}

      

So, as you can see, for each operation I have a success block, it can do some general things ... Or you can make the getter work specifics.

0


source


A dispatch_group

might be one possible solution, while maintaining an incredibly general framework and will also work with any query you want.



Rather than recreating a good example, commandhift has a good entry explaining them and a great example of how to use them

+3


source


A class AFHTTPRequestOperation

is a subclass AFURLConnectionOperation

that provides a method

+ (NSArray *)batchOfRequestOperations:(NSArray *)operations progressBlock:(void ( ^ ) ( NSUInteger numberOfFinishedOperations , NSUInteger totalNumberOfOperations ))progressBlock completionBlock:(void ( ^ ) ( NSArray *operations ))completionBlock

      

This method accepts NSArray

operations and two blocks, one to update the traverse (if necessary) and the other on completion. This method takes care of the end-of-line / end-of-line dances, which you will have to do manually. Once the operations are complete, the completion block is called and you can loop through the operations using the AFHTTPRequestOperation

and properties AFURLConnectionOperation

to grab the response data and store it in your Core Data objects.

You can create your operations like this:

AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
operation.responseSerializer = // set the serializer if you need something other than JSON

      

You can also set a completion block property if you want to store the response object in private properties and then access them in the completion block, after which you save them in your Core Data objects. This would simplify the logic required in the completion block.

To perform operations, you need to create NSOperationQueue and add operations. It will look like this:

NSArray *operations = @[op1, op2, op3...];
NSOperationQueue *downloadQueue = [[NSOperationQueue alloc] init];
[downloadQueue addOperations:operations waitUntilFinished:NO];

      

+2


source


Updated answer

In this case, you can probably write a more general method that recursively iterates through an array containing your request path:

// --------------------------------------------------------
// Header file
// --------------------------------------------------------
@property (nonatomic, strong) NSMutableArray *arrRequestURLs;

// --------------------------------------------------------
// Implementation file
// --------------------------------------------------------
-(void)viewDidLoad
{
    ...

    self.arrRequestURLs = [[NSMutableArray alloc] init];

    [self.arrRequestURLS addObject:@[@"http://api.myserver.com/api/actors"];
    [self.arrRequestURLS addObject:@[@"http://api.myserver.com/api/directors"];
    [self.arrRequestURLS addObject:@[@"http://api.myserver.com/api/photographers"];

    [self performRequestQueue:self.arrRequestURLS onSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {

        // all request completed successfully do something here

    } onFailure: ^(AFHTTPRequestOperation *operation, NSError *error) {
        NSLog(@"Failed to complete all request");
    } onEachSuccess: ^(AFHTTPRequestOperation *operation, id *responseObject, NSString *sourceRequestURL) {

        // Scroll down to bottom see method implementation
        [self handleIntermediateSuccessOperation:operation 
                                      Response:responseObject 
                              ForRequestURL:sourceRequestURL];
    }];
}

// ------------------------------------------------------------------------------
// A recursive method that executes each request string in the passed in array
// and removes from array after successfully performing request, then calls itself
// again until arrRequestURLs is empty.
// ------------------------------------------------------------------------------
-(void)performRequestQueue:(NSMutableArray *)arrRequestURLs 
                 onSuccess:(void(^)(AFHTTPRequestOperation *operation, id responseObject))success 
                 onFailure:(void(^)(AFHTTPRequestOperation *operation, NSError *error))failure
                 onEachSuccess: (void(^)(AFHTTPRequestOperation *operation, id *responseObject, NSString *sourceRequestURL))intermediateSuccess
{
    if(arrRequestURLs.count > 0)
    {
        // make request
        [manager GET:arrRequestURLs[0] parameters:nil 
        success:^(AFHTTPRequestOperation *operation, id responseObject) {

           // call intermediate success block if any
           if(intermediateSuccess)
           {
               intermediateSuccess(operation, responseObject, arrRequestURLs[0]);
           }

           // remove the current str from array
           [arrRequestURLs removeObjectAtIndex:0];

            // check to see if there more request
            if(arrRequestURLs.count > 0)
            {
                // recursively call this method again to perform next URL string from array
                [self performRequestQueue:arrRequestURLs onSuccess:success onFailure:failure];
            }
            else
            {
                // no more request in arrRequestURLs, call finish block if any
                if(success)
                {
                    success(operation, responseObject);
                }
            }           
        } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
            if(failure)
            {
                failure(operation, error);
            }
        }];
    }
}


// ------------------------------------------------------------------------------
// This method here handles the in-between success block for each operation
// including the last final success block.
// ------------------------------------------------------------------------------
-(void)handleIntermediateSuccessOperation:(AFHTTPRequestOperation *)operation 
                                          Response:(id)responseObject
                                  ForRequestURL:(NSString *)sourceRequestURL
{
    // ------------------------------------------------------------
    // Alternative, you can probably use a for loop here to get the
    // matching array index from self.arrRequestURLs and use it.
    // ------------------------------------------------------------
    if([sourceRequestURL isEqualToString:self.arrRequestURLs[0]])
    {
        // success block for actors, do something here
    }
    else if([sourceRequestURL isEqualToString:self.arrRequestURLs[1]])
    {
        // success block for directors, do something here
    }
    else
    {
        // success block for photographers, do something here
    }
}

      

Original Answer

You can link your request:

[manager GET:[NSString stringWithFormat:@"%@/api/actors", [NSString stringWithUTF8String:kBaseURL]] parameters:nil 
success:^(AFHTTPRequestOperation *operation, id responseObject) {
  // Core Data saving

    [manager GET:[NSString stringWithFormat:@"%@/api/directors", [NSString stringWithUTF8String:kBaseURL]] parameters:nil 
    success:^(AFHTTPRequestOperation *operation, id responseObject) {
      // Core Data saving


        [manager GET:[NSString stringWithFormat:@"%@/api/photographers", [NSString stringWithUTF8String:kBaseURL]] parameters:nil 
        success:^(AFHTTPRequestOperation *operation, id responseObject) {
          // Core Data saving

            // ---------------------------------------------
            // Finished downloading all data
            // do something here 
            // ---------------------------------------------


        } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
            NSLog(@"Error: %@", error);
        }];


    } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
        NSLog(@"Error: %@", error);
    }];

} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
    NSLog(@"Error: %@", error);
}];

      

+1


source


If you only have a few requests, you can nest the following operations inside success blocks. If you have more operations than a few, I suggest you use NSOperationQueue.

0


source


try this:

    NSInteger successCount;

AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
    [manager GET:someUrl parameters:nil 
success:^(AFHTTPRequestOperation *operation, id responseObject) {
  successCount++;
  if(successCount == 3){
     //do stuff
  }
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
    NSLog(@"Error: %@", error);
}];

[manager GET:someUrl parameters:nil 
success:^(AFHTTPRequestOperation *operation, id responseObject) {
  successCount++;
  if(successCount == 3){
     //do stuff
  }
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
    NSLog(@"Error: %@", error);
}];

[manager GET:someUrl parameters:nil 
success:^(AFHTTPRequestOperation *operation, id responseObject) {
  successCount++;
  if(successCount == 3){
     //do stuff
  }
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
    NSLog(@"Error: %@", error);
}];

      

0


source







All Articles