So I'm rewriting an app for iOS 7 with AFNetworking 2.0 and I'm running into the issue of sending a batch of requests at once and tracking their progress. In the old AFNetworking there was the enqueueBatchOfHTTPRequestOperations:progressBlock:completionBlock: method on AFHTTPClient, this is clearly refactored out and I'm a bit confused on how to enqueue multiple requests.
I have created a subclass of AFHTTPSessionManager and I'm using the POST:... and GET:... methods to communicate with the server. But I can't find anything in the code and/or docs to enqueue multiple requests at once like with the old AFHTTPClient.
The only thing I can find is the undocumented batchOfRequestOperations:progressBlock:completionBlock: method on AFURLConnectionOperation, but that looks like the iOS 6 way of doing this.
Clearly I'm missing something in the new NSURLSession concept that I should use to batch requests or looking over a new AFNetworking feature. Hope someone can help me on the right track here!
tl;dr: How can I send a batch of requests with my AFHTTPSessionManager subclass?
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.
Currently, NSURLSession tasks are not suitable for the same kind of patterns request operations use. See the answer from Mattt Thompson here regarding this issue.
Direct answer: if you need dependencies or batches, you'll still need to use request operations.
Related
I have this bool method that returns a yes or no for an inputted string.
I'm successfully able to return a YES or a NO, but I cannot seem to able to make a network connection and return a YES or a NO depending on the server's response.
I tried using __block and I don't feel like that will wait for the web request to finish, is there a way to return YES or NO in the success block without it giving me the error:
Incompatible block pointer types sending 'BOOL(^)(NSURLSessionTask*__strong, NSError __strong' to parameter of the type 'void(^)(NSURLSessionTask...)
-(BOOL)customResponseForString:(NSString *)text {
__block BOOL response_available;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
manager.responseSerializer = [AFHTTPResponseSerializer serializer];
[manager.responseSerializer setAcceptableContentTypes:[NSSet setWithObject:#"text/plain"]];
[manager GET:[NSString stringWithFormat:#"http://example.com/response.php?input=%#", text] parameters:nil success:^(NSURLSessionDataTask *task, id responseObject) {
NSDictionary *response = [NSJSONSerialization JSONObjectWithData:responseObject options:NSUTF8StringEncoding error:nil];
response_available = (BOOL)response[#"response_available"];
if (response_available) {
[session sendTextSnippet:response[#"response"] temporary:NO scrollToTop:NO dialogPhase:#"Summary"];
} else {
response_available = NO;
}
[session sendTextSnippet:[[NSString alloc] initWithData:responseObject encoding:NSUTF8StringEncoding] temporary:NO scrollToTop:NO dialogPhase:#"Summary"];
[session sendRequestCompleted];
} failure:^(NSURLSessionDataTask *task, NSError *error) {
//return NO;
}];
});
return response_available;
}
Your block definition syntax is probably erroneous, because you can definitely return a BOOL along other parameters in a block.
- (void)fetchCurrentUserWithCompletion:(void (^)(BOOL success, User *user))completion;
This method would be called like this:
[self.userProfileController fetchCurrentUserWithCompletion:^(BOOL success, User *user) {
if (success) {
NSLog(#"Current User Name: %#", user.fullName);
}
}];
If you use AFNetworking, check the AFHTTPRequestOperation object that handle completionBlocks:
[requestOperation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
User *user = [self userFromResponseObject:responseObject];
if (completion) completion(YES, user);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
if (completion) completion(NO, user);
}];
Because you are implicitly initializing response_available to NO and then using an async GCD call, your method as written will always immediately return NO without waiting for the request to finish. Note: switching to dispatch_sync won't help either because AFNetworking will queue the GET request asynchronously either way.
Best Approach
Add a completion block argument to customResponseForString:. Then simply execute your completion block in the success or failure blocks of the AFHTTPRequestOperation.
Workable Approach (use caution!)
It is possible to make customResponseForString: wait for a response to the network request, but you will have significant issues if it is ever called from the main thread.
First you create a dispatch group and tell it you are starting some long-running work:
dispatch_group_t networkGroup = dispatch_group_create();
dispatch_group_enter(networkGroup);
Then you need to make your network request and when it completes tell the group that the work is finished with dispatch_group_leave():
[manager GET:[NSString stringWithFormat:#"http://example.com/response.php?input=%#", text] parameters:nil success:^(NSURLSessionDataTask *task, id responseObject) {
NSDictionary *response = [NSJSONSerialization JSONObjectWithData:responseObject options:NSUTF8StringEncoding error:nil];
response_available = (BOOL)response[#"response_available"];
if (response_available) {
[session sendTextSnippet:response[#"response"] temporary:NO scrollToTop:NO dialogPhase:#"Summary"];
} else {
response_available = NO;
}
[session sendTextSnippet:[[NSString alloc] initWithData:responseObject encoding:NSUTF8StringEncoding] temporary:NO scrollToTop:NO dialogPhase:#"Summary"];
[session sendRequestCompleted];
dispatch_group_leave(networkGroup);
} failure:^(NSURLSessionDataTask *task, NSError *error) {
response_available = NO;
dispatch_group_leave(networkGroup);
}];
Before your original method returns, tell it to wait for the entire group to finish processing:
dispatch_group_wait(networkGroup, DISPATCH_TIME_FOREVER);
return response_available;
You could adjust this time interval as needed or leave it at DISPATCH_TIME_FOREVER to let the network request time out on its own.
I have a question on which is best way or the correct way to send AFNetworking results to controller. Is it via delegate or notification?
I created a class to handle make API calls that has the code below. So if imported this class to another controller and call this method to make API call. Should I do delegate or notification?
I have read www.raywenderlich.com/59255/afnetworking-2-0-tutorial and it is using delegates. I also been watched CodeSchool tutorial, which they used notification from Model to Controller.
I added the code below in a hope to better show my question.
AFHTTPSessionManager *manager = [[AFHTTPSessionManager alloc] initWithBaseURL:baseURL];
// notification way inside the BLOCK
[ manager GET:path parameters:params
success:^(NSURLSessionDataTask *operation, id responseObject) {
[ [NSNotificationCenter defaultCenter] postNotificationName:notificationName
object:nil
userInfo:responseObject ];
} failure:^(NSURLSessionDataTask *operation, NSError *error) {
[ [NSNotificationCenter defaultCenter] postNotificationName:notificationName
object:nil ];
}];
// delegate way inside the BLOCK
[ manager GET:path parameters:params
success:^(NSURLSessionDataTask *operation, id responseObject) {
if ([delegate respondsToSelector:#selector(getUserFeedsDidFinish:resultDict:)])
{
[delegate performSelector:#selector(getUserFeedsDidFinish:resultDict:) withObject:self withObject:resultDict];
}
} failure:^(NSURLSessionDataTask *operation, NSError *error) {
if ([delegate respondsToSelector:#selector(getUserFeeds:didFailWithResultDict:)]) {
[delegate performSelector:#selector(getUserFeeds:didFailWithResultDict:)
withObject:self
withObject:[NSDictionary dictionaryWithObject:error.userInfo forKey:KEY_ERRORS]];
}
}];
I will recommend use blocks, how? I will write a service for you, this one is wrote in a class called Connection:
+(void)requestLocation:(NSString*)googleReference completionBlock:(void (^)(NSString * coordinates, NSError * error)) handler{
NSString * urlString = #"https://maps.googleapis.com/maps/";
NSMutableDictionary * parametersDictionary = [NSMutableDictionary dictionary];
[parametersDictionary setObject:googleReference forKey:#"reference"];
[parametersDictionary setObject:#"true" forKey:#"sensor"];
[parametersDictionary setObject:#"key(it is not)" forKey:#"key"];
AFHTTPClient *HTTPClient = [AFHTTPClient clientWithBaseURL:[NSURL URLWithString:urlString]];
NSURLRequest *URLRequest = [HTTPClient requestWithMethod:#"GET" path:#"api/place/details/json" parameters:parametersDictionary];
AFHTTPRequestOperation *requestOperation = [[AFHTTPRequestOperation alloc] initWithRequest:URLRequest];
[requestOperation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
NSError * error = nil;
NSDictionary * response = [NSJSONSerialization JSONObjectWithData:responseObject options:NSJSONReadingMutableContainers error:&error];
NSDictionary * dicGeo = [((NSDictionary*)[response objectForKey:#"result"]) objectForKey:#"geometry"];
NSDictionary * coords = [dicGeo objectForKey:#"location"];
NSNumber * lat = [coords objectForKey:#"lat"];
NSNumber * lng = [coords objectForKey:#"lng"];
NSString * coordinates = [NSString stringWithFormat:#"%#,%#", lat.description, lng.description];
handler(coordinates, error);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"%#", error);
}];
[requestOperation start];
}
Then to call this service:
[Connection requestLocation:#"google reference (it is not)" completionBlock:^(NSString *coordinates, NSError *error) {
//Your code with results.
}
I've only scratched the surface with AFNetworking. From what I've seen, most of it seems to use a third approach, blocks.
Blocks are somewhat new, and different than both delegates and notifications.
Blocks are an extension to C function pointers that let you pass code into a method when you call it.
A common design pattern using blocks is to create a method that takes a completion block. A completion block is a piece of code that gets invoked when an async request is completed.
Take the AFNewtworking method HTTPRequestOperationWithRequest as an example. That method takes a success block, that gets called if the request succeeds, and a failure block, that gets called if the request fails.
Block is the easiest way to use IMO. You don't need to implement extra delegate methods or you don't need any conformations.
Basically define your wrapper like this.
typedef void(^SampleRequestCompletion)(NSError *error, id data);
- (void)GET:(NSString *)URLString
parameters:(NSDictionary *)parameters
completion:(SampleRequestCompletion)completion
{
[self GET:URLString parameters:parameters success:^(AFHTTPRequestOperation *operation, id responseObject) {
// Do what you want
if (completion) {
completion(nil, data);
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
// Failure case
if (completion) {
completion(error,nil);
}
}];
}
And call this method from any objects like this,
[self GET:path parameters:dictionary completion:^(NSError *error, id data) {
}];
So you can manage what to do whenever the call ends with success or failure.
As the tutorial recommended, we can extract the web service related code into a module which acts more like a model level thing. Considering the communication between the network module and views, view invoke/start the request on a singleton web service client, once response back the usual workflow would be send the result to view controller and show the data in the views. We don't need to return anything back to network module.
So this workflow is more like a notification than delegation. And set the V as the M's delegate, it's weird.
Notification : Hey, man, I have done my job, it's your turn.
Delegation: Hey, man, I have done lots, now I need you cover/back up/provide me some tasks, then I will continue/complete the work.
In some situations, it's difficult to choose which one better. For AFNetworking, I thought the Notification approach better.
I'm trying to figure out a way to download multiple images with AFNewtorking 2.0. I've read a lot of posts here in SO, but can't find the answer I'm looking for, hope you guys can help me.
The problem is that I want to know when all of the downloads finished and if all images where downloaded.
So I have an array with image URL's ant trying to do something like this.
for(NSString *photoUrlString in self.photos){
NSURL *url = [NSURL URLWithString:photoUrlString];
AFHTTPRequestOperation *requestOperation = [[AFHTTPRequestOperation alloc] initWithRequest:[NSURLRequest requestWithURL:url]];
requestOperation.responseSerializer = [AFImageResponseSerializer serializer];
[requestOperation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Image error: %#", error);
}];
[requestOperation start];
}
I've found some answers with putting these requests into a queue and setting max concurrent operations to 1. But don't know how that works really.
Any help is appreciated, thanks in advance!
for(Photo *photo in array){
//form the path where you want to save your downloaded image to
NSString *constPath = [photo imageFullPath];
//url of your photo
NSURL *url = [NSURL URLWithString:photo.serverPath];
AFHTTPRequestOperation *op = [[AFHTTPRequestOperation alloc] initWithRequest:[NSURLRequest requestWithURL:url]];
op.responseSerializer = [AFImageResponseSerializer serializer];
op.outputStream = [NSOutputStream outputStreamToFileAtPath:constPath append:NO];
op.queuePriority = NSOperationQueuePriorityLow;
[op setDownloadProgressBlock:^(NSUInteger bytesRead, long long totalBytesRead, long long totalBytesExpectedToRead){
}];
op.completionBlock = ^{
//do whatever you want with the downloaded photo, it is stored in the path you create in constPath
};
[requestArray addObject:op];
}
NSArray *batches = [AFURLConnectionOperation batchOfRequestOperations:requestArray progressBlock:^(NSUInteger numberOfFinishedOperations, NSUInteger totalNumberOfOperations) {
} completionBlock:^(NSArray *operations) {
//after all operations are completed this block is called
if (successBlock)
successBlock();
}];
[[NSOperationQueue mainQueue] addOperations:batches waitUntilFinished:NO];
Try this:
// _group, _queue are iVar variable
dispatch_group_t *_group = dispatch_group_create();
dispatch_queue_t *_queue = dispatch_queue_create("com.company.myqueue2", NULL);
// all files download
for(int i = 0 ; i < numberOfFileDownloads; i++){
dispatch_group_async(_group, _queue, ^{
// here is background thread;
// download file
});
}
// all files are download successfully, this method is called
dispatch_group_notify(_group, _queue, ^{
}
Check out +[AFURLConnectionOperation batchOfRequestOperations:progressBlock:completionBlock:]
Although it's not documented, implementation is self-explanatory. Also it allows you to monitor the progress.
You will need to have an array of HTTP operations prior to using this method (this is if you decided to stick to NSURLConnection-based implementation of AFNetworking).
I've got subclass of AFHTTPClient
The main idea is that i call all API through my singleton of AFHTTPClient subclass, and all requests goes through 1 points for error handling and HUD displaying.
This is entry point for every API calls:
-(void) makeRequestWithPath:(NSString*) path andParams:(NSDictionary*) params
success:(void (^)( id JSON, AFHTTPRequestOperation *operation)) success
failure:(void (^)( NSError *error)) failure
And i've got many methods for API calls something like that:
-(void) getListMainTreeWithSuccess:(void (^)( id JSON, AFHTTPRequestOperation *operation)) success
failure:(void (^)( NSError *error)) failure
{
[self makeRequestWithPath:#"objects/selectlist" andParams:nil success:^(id JSON, AFHTTPRequestOperation *operation) {
success(JSON,operation);
} failure:^(NSError *error) {
failure(error);
}];
}
This works just fine for my needs. But i faced problem that i need to make serial request in loop through my AFHTTPClient subclass and make some action when all of them are finished , I found method
-(void)enqueueBatchOfHTTPRequestOperationsWithRequests:(NSArray *)urlRequests
progressBlock:(void (^)(NSUInteger numberOfFinishedOperations, NSUInteger totalNumberOfOperations))progressBlock
completionBlock:(void (^)(NSArray *operations))completionBlock
which should solve my issue, but the problem is that i call all methods through AFHTTPClient and it's methods getPath: and postPath: and previous way forces me to rewrite everything and makes my subclass completely useless, because I need to add there NSArray of AFHTTPRequestoperation, which is not possible to construct or extract from my subclass and my methods. Previously i tried to use __block 's to synchronise requests with semaphore and something else but i failed to get what i need, please help me!
UPDATE:
It seems that it is not possible to even use enqueueBatchOfHTTPRequestOperations method (even with rewriting all my code) because this method needs array of http request operations, but it's not possible to construct POST request with them.
I solved this with an increment/decrement pending download system and tied the HUD to that.
[networkStatus beginNetworkActivity];
[client someRESTActionWithCompletion:^(id object, NSError *error) {
[networkStatus endNetworkActivity];
if (error) {
// Handle the error ...
}
if (![networkStatus hasNetworkActivity]) {
// All downloads have finished
}
}];
I keep the network status object separate which from the AFHTTPClient subclass, but it can be built into the client if that's what you want.
Network status keeps an internal counter. -beginNetworkActivity increments the counter, if the counter was 0, then it displays a HUD. -endNetworkActivity decrements the counter, if the counter becomes 0, then it dismisses the HUD. -hasNetworkActivity returns YES if the counter greater than 0.
Other Notes: I combine the success and failed callbacks into a single completion callback. I keep the network status logic separate from the client because sometime I'll use a singleton network status object, sometimes I'll use a created instance, sometimes I won't use one at all. It all depends on the needs to the higher level logic.
Again, as #MikePollard said, create AFHTTPRequestOperation using
[AFHHTPClient HTTPRequestOperationWithRequest:success:failure:]
For this method create NSURLRequest using (or use another one, pick which one is suitable for you). Here you can also specify, which method to use POST, GET or any other.
[AFHTTPClient requestWithMethod:
path:
parameters:]
After that save all operation to an NSArray, and schedule them using:
[AFHTTPClient enqueueBatchOfHTTPRequestOperationsWithRequests:
progressBlock:
completionBlock:]
Code example:
NSMutableArray *ops = [NSMutableArray new];
NSMutableURLRequest *request1 = [[AFHTTPClient sharedClient] requestWithMethod:#"GET"
path:#"MyEndpoint"
parameters:#{#"key1": #"value"}];
AFHTTPRequestOperation *op1 = [[AFHTTPClient sharedClient] HTTPRequestOperationWithRequest:request1
success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"Success!");
}
failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Failure!");
}];
[ops addObject:op1];
NSMutableURLRequest *request2 = [[AFHTTPClient sharedClient] requestWithMethod:#"POST"
path:#"MyAnotherEndpoint"
parameters:#{#"key2": #(104)}];
AFHTTPRequestOperation *op2 = [[AFHTTPClient sharedClient] HTTPRequestOperationWithRequest:request2
success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"Success!");
}
failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Failure!");
}];
[ops addObject:op2];
[[AFHTTPClient sharedClient] enqueueBatchOfHTTPRequestOperationsWithRequests:ops
progressBlock:^(NSUInteger numberOfFinishedOperations, NSUInteger totalNumberOfOperations) {
NSLog(#"numberOfFinishedOperations: %d totalNumberOfOperations %d",
numberOfFinishedOperations,
totalNumberOfOperations);
}
completionBlock:^(NSArray *operations) {
NSLog(#"All operation compelted!");
}];
I download asynchronously some object, I store it in array. Next for each object I download some coordinates with geocoding (it is also asynchronously), and update my database for each object with new parameters which is coordinate. My method looks like this:
- (void)downloadObjectsWithTitle:(NSString *)title andHandler:(void(^)(NSMutableDictionary *result))handler {
AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:url];
NSMutableURLRequest *request = [httpClient requestWithMethod:#"GET"
path:nil
parameters:nil];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
[httpClient registerHTTPOperationClass:[AFHTTPRequestOperation class]];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
//I get here array of objects
//now for each object I want to download geocoding localization so i called another asynchronyous method getLocationWithTitle:andHandler;
for(int i = 0; i < resutArray.count; i++) {
[self downloadLocationWithString:[dictionary objectForKey:#"string"] andHandler:^(NSMutableDictionary *result) {
//update database;
}];
}
handler(dictionary);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", error);
}];
[operation start];
}
My question is how to downalod coordinates for each object and that fire:
handler(dictionary);
so wait for each coordinates download (for each object) before quit method (fire handler).
Thnaks for all sugestions.
Maintain a count of all the tasks. When it's zero you're done.
Assuming you're using dispatch_async in downloadLocationWithString: on a concurrent queue:
dispatch_barrier_async(queue, ^{
// will only be called after all the blocks submitted to queue have finished.
}];
(If you're using serial queue, simply call handler at the last line of the last block)
Try a global flag. set NO first. In download block, after download complete set flag to yes. You can check that flag.