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
source to share
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.
source to share
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
source to share
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];
source to share
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);
}];
source to share
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);
}];
source to share