Max concurrent instances of a NSURLSessionDataTask - ios

I have a function that calls an API with NSURLSessionDataTask you can see it here:
- (void)getExplorerUrl:(void (^)(NSString *))measurement_url {
NSString *path = [NSString stringWithFormat:#"https://api.ooni.io/api/v1/measurements?report_id=%#&input=%#", self.report_id, self.url_id.url];
NSURL *url = [NSURL URLWithString:path];
NSURLSessionDataTask *downloadTask = [[NSURLSession sharedSession] dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if (!error) {
NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
NSArray *resultsArray = [dic objectForKey:#"results"];
if ([resultsArray count] == 0)
measurement_url(nil);
measurement_url([[resultsArray objectAtIndex:0] objectForKey:#"measurement_url"]);
}
else {
// Fail
measurement_url(nil);
NSLog(#"error : %#", error.description);
}
}];
[downloadTask resume];
}
This function uses a completion handler to return a value when the async call is finished.
Now I want a for cycle to loop many objects and call this API for every object:
for (Measurement *measurement in [Measurement measurementsWithJson]){
[measurement getExplorerUrl:^(NSString *measurement_url) {
if (measurement_url != nil){
//Do something
NSLog(#"%# measurement_url %#",measurement.Id, measurement_url);
}
else {
NSLog(#"%# measurement_url null", measurement.Id);
}
}];
}
Is there a way to set a max concurrent async calls to 10? And then execute the next call as soon as one call finishes.

I agree with #Rob that he can create his own configuration for URLSession. However, if this sharedSession is used across different jobs and he wanted this job to run with max concurrent async calls to 10, I would suggest to use either NSOperationQueue or dispatch_semaphore to solve this problem. Please refer to the example below to have draftily understand on these approaches
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
queue.maxConcurrentOperationCount = 10;
for (int i = 1; i <= 30; i++) {
[queue addOperationWithBlock:^{
NSLog(#"[Q] %d", i);
sleep(1);
}];
}
or
dispatch_queue_t q = dispatch_queue_create("q.q", DISPATCH_QUEUE_CONCURRENT);
dispatch_semaphore_t s = dispatch_semaphore_create(10);
for (int i = 1; i <= 30; i++) {
dispatch_async(q, ^{
NSLog(#"[Q] %d", i);
sleep(1);
dispatch_semaphore_signal(s);
});
}
You can observe from the console to see the results. Basically the 2 approaches will perform maximum 10 calls at the same time, and as long as one finished, others will be enter to the execution queue.
I hope this will help you to address your problem. Can have any discussion as needed.!!!

Related

NSOperationOperationQueue cancelAllOperations method won't stop operations

How to cancel all operations in NSOperationQueue? I used cancelAllOperations method, but it didn't work, the NSOperationQueue is still calling server to upload photo.
I put every single connection on NSOperationQueue with loop.
- (void)sendingImage:(NSArray *)imgArray compression:(CGFloat)compression
{
hud = [MBProgressHUD showHUDAddedTo: self.view animated: YES];
hud.label.text = [NSString stringWithFormat: #"Waiting for Loading"];
[hud.button setTitle: #"Cancel" forState: UIControlStateNormal];
[hud.button addTarget: self action: #selector(cancelWork:) forControlEvents: UIControlEventTouchUpInside];
__block int photoFinished = 0;
self.queue = [[NSOperationQueue alloc] init];
self.queue.maxConcurrentOperationCount = 5;
[self.queue addObserver: self forKeyPath: #"operations" options: 0 context: NULL];
NSBlockOperation *operation = [[NSBlockOperation alloc] init];
__weak NSBlockOperation *weakOperation = operation;
__block NSString *response = #"";
for (int i = 0; i < imgArray.count; i++) {
operation = [NSBlockOperation blockOperationWithBlock:^{
[self uploadingPhoto];
}];
[operation setCompletionBlock:^{
NSLog(#"Operation 1-%d Completed", i);
photoFinished++;
dispatch_async(dispatch_get_main_queue(), ^{
hud.label.text = [NSString stringWithFormat: #"%d photo complete uploading", photoFinished];
});
}];
[self.queue addOperation: operation];
}
}
I want to press cancel button on MBProgressHUD to first canceled all the NSURLSessionDataTask and then cancel all operations, but didn't work.
- (void)cancelWork:(id)sender {
NSLog(#"cancelWork");
NSLog(#"self.queue.operationCount: %lu", (unsigned long)self.queue.operationCount);
[session getTasksWithCompletionHandler:^(NSArray<NSURLSessionDataTask *> * _Nonnull dataTasks, NSArray<NSURLSessionUploadTask *> * _Nonnull uploadTasks, NSArray<NSURLSessionDownloadTask *> * _Nonnull downloadTasks) {
if (!dataTasks || !dataTasks.count) {
return;
}
for (NSURLSessionDataTask *task in dataTasks) {
[task cancel];
if ([self.queue operationCount] > 0) {
[self.queue cancelAllOperations];
}
}
}];
}
I used semaphore to let NSURLSession become Synchronous connection.
- (void)uploadingPhoto {
request setting above
NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
config.timeoutIntervalForRequest = 1200;
session = [NSURLSession sessionWithConfiguration: config];
dataTask = [session dataTaskWithRequest: request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
if (error == nil) {
str = [[NSString alloc] initWithData: data encoding: NSUTF8StringEncoding];
NSLog(#"str: %#", str);
}
dispatch_semaphore_signal(semaphore);
}];
NSLog(#"task resume");
[dataTask resume];
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
return str;
}
Any comments or solutions will be greatly appreciated.
An NSOperation does not by default have support for cancellation. See the class documentation. One extract is:
Canceling an operation does not immediately force it to stop what it is doing. Although respecting the value in the cancelled property is expected of all operations, your code must explicitly check the value in this property and abort as needed.
It also seems hard to implement cancellation using NSBlockOperation.

Nested multiple request with dispatch_group

I would like to make multiple requests to server to fetch posts and comments one after another. So, I created this example with dispatch_group which fetches all the POSTS serially one after another and then after it finished with the POSTS, it fetches comments one after another.
Here is a rough schema about how this works.
Fetch post 1
Fetch post 2
Fetch post 3
....
Fetch post 50
Fetch comment 1
Fetch comment 2
...
Fetch comment 50
So, all these should work serially as shown, like it fetches post 1, finishes with that and then fetches post 2 finish and so on.
The following example works fine for the purpose. But, now I would want to have a call back to actually know when syncing of 50 posts were finished and when 50 comments were finished. I tried that by adding dispatch_group_notify after for loop in requestOne and requestTwo. But, the notify method seems to be called when all the tasks have been completed. How can that be achieved ? I am not native English speaker so, please write down if I need to improve the post, I can still try :)
#interface GroupTest ()
#property (nonatomic, readonly) dispatch_group_t group;
#property (nonatomic, readonly) dispatch_queue_t serialQueue;
#end
#implementation GroupTest
- (instancetype)init
{
if (self = [super init]) {
_group = dispatch_group_create();
_serialQueue = dispatch_queue_create("com.test.serial.queue",
DISPATCH_QUEUE_SERIAL);
}
return self;
}
- (void)start
{
dispatch_async(self.serialQueue, ^{
[self requestOneCompletion:^{
NSLog(#"Request 1 completed");
}];
[self requestTwoCompletion:^{
NSLog(#"Request 2 completed");
}];
});
}
- (void)requestTwoCompletion:(void(^)(void))completion
{
for (NSUInteger i = 1; i <= 50; i++) {
dispatch_group_enter(self.group);
[self requestComment:i
completion:^(id response){
NSLog(#"%#", response);
dispatch_group_leave(self.group);
}];
dispatch_group_wait(self.group, DISPATCH_TIME_FOREVER);
}
}
- (void)requestOneCompletion:(void(^)(void))completion
{
for (NSUInteger i = 1; i <= 50; i++) {
dispatch_group_enter(self.group);
[self requestPost:i
completion:^(id response){
NSLog(#"%#", response);
dispatch_group_leave(self.group);
}];
dispatch_group_wait(self.group, DISPATCH_TIME_FOREVER);
}
}
- (void)requestComment:(NSUInteger)comment
completion:(void(^)(id))completion
{
NSString *urlString = [NSString stringWithFormat:#"https://jsonplaceholder.typicode.com/comments/%lu", (unsigned long)comment];
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *dataTask = [session dataTaskWithURL:[NSURL URLWithString:urlString]
completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
id object = [NSJSONSerialization JSONObjectWithData:data
options:0
error:nil];
completion(object);
}];
[dataTask resume];
}
- (void)requestPost:(NSUInteger)post
completion:(void(^)(id))completion
{
NSString *urlString = [NSString stringWithFormat:#"https://jsonplaceholder.typicode.com/posts/%lu", (unsigned long)post];
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *dataTask = [session dataTaskWithURL:[NSURL URLWithString:urlString]
completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
id object = [NSJSONSerialization JSONObjectWithData:data
options:0
error:nil];
completion(object);
}];
[dataTask resume];
}
#end
I think what you want to do is the following. Note that this is made so each completion block for requestTwoCompletion and requestOneCompletion will be called after all 50 calls are done. The order of the 50 calls are not guaranteed.
The main changes I made were the dispatch_group_t is local to each method and I moved dispatch_group_wait outside of the for loop. In this case it takes away the benefit of completion since the wait will block unit it is done. If you are highly insistent on completion being used and it not blocking, you can wrap this all in a dispatch_async.
- (void)requestTwoCompletion:(void(^)(void))completion
{
dispatch_group_t group = dispatch_group_create();
for (NSUInteger i = 1; i <= 50; i++) {
dispatch_group_enter(group);
[self requestComment:i
completion:^(id response){
NSLog(#"%#", response);
dispatch_group_leave(group);
}];
}
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
completion();
}
- (void)requestOneCompletion:(void(^)(void))completion
{
dispatch_group_t group = dispatch_group_create();
for (NSUInteger i = 1; i <= 50; i++) {
dispatch_group_enter(group);
[self requestPost:i
completion:^(id response){
NSLog(#"%#", response);
dispatch_group_leave(group);
}];
}
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
}
As this is in a serial queue, the way this will work is requestOneCompletion will finish all 50, and then requestTwoCompletion will run all 50 next.

Asynchronous request running slowly - iOS

I have an app which downloads a set of photos from a server. I am using an Asynchronous request because I don't want the UI to be blocked. However, I am finding that the request is very slow and takes ages to load.
I know you can set the queue type to [NSOperationQueue mainQueue] but that just puts the Asynchronous request back on the main thread which defeats the whole point of making the request Asynchronously in the first place.
Is there anyway to speed up the request or to tell iOS: "Run this request in the background, but do it ASAP, don't leave it till the end of the queue"???
Here is my code:
// Set up the photo request.
NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:PHOTO_URL, pass_venue_ID, PHOTO_CLIENT_ID, PHOTO_CLIENT_SECRET]];
NSURLRequest *urlRequest = [NSURLRequest requestWithURL:url];
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
// Begin the asynchromous image loading.
[NSURLConnection sendAsynchronousRequest:urlRequest queue:queue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
if (error == nil) {
// Convert the response data to JSON.
NSError *my_error = nil;
NSDictionary *feed = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableLeaves error:&my_error];
// Check to see if any images exist
// for this particular place.
int images_check = [[NSString stringWithFormat:#"%#", [[[feed objectForKey:#"response"] valueForKey:#"photos"] valueForKey:#"count"]] intValue];
if (images_check > 0) {
// Download all the image link properties.
images_prefix = [[[[feed objectForKey:#"response"] valueForKey:#"photos"] valueForKey:#"items"] valueForKey:#"prefix"];
images_suffix = [[[[feed objectForKey:#"response"] valueForKey:#"photos"] valueForKey:#"items"] valueForKey:#"suffix"];
images_width = [[[[feed objectForKey:#"response"] valueForKey:#"photos"] valueForKey:#"items"] valueForKey:#"width"];
images_height = [[[[feed objectForKey:#"response"] valueForKey:#"photos"] valueForKey:#"items"] valueForKey:#"height"];
// Set the image number label.
number_label.text = [NSString stringWithFormat:#"1/%lu", (unsigned long)[images_prefix count]];
// Download up to 5 images.
images_downloaded = [[NSMutableArray alloc] init];
// Set the download limit.
loop_max = 0;
if ([images_prefix count] > 5) {
loop_max = 5;
}
else {
loop_max = [images_prefix count];
}
for (NSUInteger loop = 0; loop < loop_max; loop++) {
// Create the image URL.
NSString *image_URL = [NSString stringWithFormat:#"%#%#x%#%#", images_prefix[loop], images_width[loop], images_height[loop], images_suffix[loop]];
// Download the image file.
NSData *image_data = [NSData dataWithContentsOfURL:[NSURL URLWithString:image_URL]];
// Store the image data in the array.
[images_downloaded addObject:image_data];
}
// Load the first image.
[self load_image:image_num];
}
else if (images_check <= 0) {
// error...
}
}
else {
// error
}
}];
Thanks for your time, Dan.
i think your problem isnt the request running slow, its that you are updating UI elements not on the main thread, surround any UI updates (like setting the text on labels) with
dispatch_sync(dispatch_get_main_queue(), ^{
<#code#>
});
As Fonix said its not iOS that responding slow but dataWithContentsOfURL doesn't work in background thread. Apple's recommendation is that you should use NSURLConnection asynchronously with delegates
- didReceiveResponse
- didReceiveData
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:theURL cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:_mAuthenticationTimeoutInterval];
In these methods you can make use of chunks of data as well.
If you actually want these multiple downloads to be faster you should use parallel downloading using NSOperationQueue. You can refer enter link description here
I think a good solution could be using AFNetworking when combined with NSOperation, check this code I wrote to do more than one operation asynchronously
NSMutableArray *operations = [[NSMutableArray alloc] init];
for(NSObject *obj in caches) {
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
[request setURL:url];
//...set up your mutable request options here
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
operation.responseSerializer = [AFJSONResponseSerializer serializer];
operation.responseSerializer.acceptableContentTypes = [NSSet setWithObject:#"application/json"];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
NSInteger statusCode = operation.response.statusCode;
if(statusCode==200) {
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"API Call error:%#", error.localizedDescription);
}];
[[requestManager operationQueue] addOperation:operation];
[operations addObject:operation];
if([operations count] >= MAX_API_CALL) break;
}
[AFHTTPRequestOperation batchOfRequestOperations:operations progressBlock:^(NSUInteger numberOfFinishedOperations, NSUInteger totalNumberOfOperations) {
} completionBlock:^(NSArray *operations) {
NSError *error;
for (AFHTTPRequestOperation *op in operations) {
if (op.isCancelled){
}
if (op.responseObject){
// process your responce here
}
if (op.error){
error = op.error;
}
}
}];

Make periodic server requests with NSURLSessionDataTask and dispatch_source timer

Need some help and explanation, because i'm really stuck in my question. i need to make this:
1) I make one request to the server, get some response and then i want to make another request every 7 seconds(example). also get some response. if it satisfy several conditions -> stop timer and do some stuff.
Main problem is that timer never stops, despite the fact that all in all i get response right. i assume that i use GCD incorrectly. because in debug this code behaves really strange.
What i have done:
This is my request function(it became like this after i read about 50 links how to do similar things)
-(void)makeRequestWithURL:(NSString*)urlString andParams:(NSString*)params andCompletionHandler:(void(^)(NSDictionary *responseData, NSError *error))completionHnadler{
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration];
NSURL *url = [NSURL URLWithString:urlString];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url
cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60.0];
request.HTTPMethod = #"POST";
request.HTTPBody = [params dataUsingEncoding:NSUTF8StringEncoding];
NSURLSessionDataTask *postDataTask = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if (completionHnadler) {
if (error) {
dispatch_async(dispatch_get_main_queue(), ^{
completionHnadler(nil, error);
});
} else {
NSError *parseError;
json = [NSJSONSerialization JSONObjectWithData:data
options:NSJSONReadingMutableContainers
error:&parseError];
dispatch_async(dispatch_get_main_queue(), ^{
completionHnadler(json, parseError);
});
}
}
}];
[postDataTask resume]; }
I create my timer like this:
dispatch_source_t CreateDispatchTimer(uint64_t interval,
uint64_t leeway,
dispatch_queue_t queue ,
dispatch_block_t block) {
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
if (timer) {
// Use dispatch_time instead of dispatch_walltime if the interval is small
dispatch_source_set_timer(timer, dispatch_walltime(NULL, 0), interval, leeway);
dispatch_source_set_event_handler(timer, block);
dispatch_resume(timer);
}
return timer; }
and called it like this:
-(void)checkForPassenger {
timerSource = CreateDispatchTimer(7ull * NSEC_PER_SEC, 1ull * NSEC_PER_SEC, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
if([self getNotificationsRequest] == YES) {
dispatch_source_cancel(timerSource);
} else {
NSLog(#"go on timer");
}
NSLog(#"Driver checked for passenger!");
}); }
this is the code of periodic response:
-(BOOL)getNotificationsRequest {
NSString *urlString = #"http://primetime.by/temproad/do";
NSString *params = [NSString stringWithFormat:#"event={\"type\": \"in.getNotifications\"}&session_id=%#",session_id];
[self makeRequestWithURL:urlString andParams:params andCompletionHandler:^(NSDictionary *responseData, NSError *error) {
if ([[responseData objectForKey:#"rc"] intValue] == 0) {
NSArray *temp_notifications = [responseData objectForKey:#"notifications"];
if (temp_notifications.count != 0) {
notification = [[Notification alloc] initWithNotification:[[responseData objectForKey:#"notifications"] objectAtIndex:0]];
}
}
}];
if (notification) {
return YES;
} else {
return NO;
} }
and this is what i do in main request:
[self makeRequestWithURL:urlString andParams:params andCompletionHandler:^(NSDictionary *responseData, NSError *error) {
if ([[responseData objectForKey:#"rc"] intValue] == 0) {
route = [[Route alloc] initWithData:responseData];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self checkForPassenger];
});
}
}];
NSLog(#"bye");
maybe explanation is bad so i can answer any question.
thx

Problems with dispatch iOS

I'm new to iOS and I have trouble understanding and applying well dispatch ... I have an application I need to query a website (api) within a for loop, the end of that cycle I need to make further inquiries in another cycle, and finally, at the end of both cycles need to switch views.
I now have this code (after much trial and error but still does not work):
dispatch_queue_t queue = dispatch_queue_create("threadServicios", DISPATCH_QUEUE_SERIAL);
dispatch_group_t group = dispatch_group_create();
dispatch_group_enter(group);
dispatch_async(queue, ^(void) {
NSLog(#"llego a buscar servicios por local");
for (NSDictionary *local in _arrLocalesTmp) {
[self getListaServiciosPorLocal:[local objectForKey:#"idLocal"]];
//this function calls another function that consumes a web service and get a json
}
procced = YES;
NSLog(#"llego a buscar profesionales por local");
for (NSDictionary *local in _arrLocalesTmp) {
[self getListaProfesionalesPorLocal:[local objectForKey:#"idLocal"]];
//this function calls another function that consumes a web service and get a json
}
procced2 = YES;
dispatch_group_leave(group);
});
dispatch_group_notify(group, dispatch_get_main_queue(),^{
NSLog(#"dispatch procced 1");
if (procced && procced2) {
[self setFormularioConsultaCompleto];
}
});
The function [self getListaServiciosPorLocal: [Local objectForKey: # "idLocal"]]; is as follows:
dispatch_async(dispatch_get_main_queue(), ^(void) {
NSURL *url = [NSURL URLWithString:urlConnection];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration];
sessionConfiguration.timeoutIntervalForRequest = 30;
sessionConfiguration.timeoutIntervalForResource = 60;
NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfiguration];
NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
__block NSError *jsonError;
NSHTTPURLResponse *urlResponse = (NSHTTPURLResponse *) response;
if(!error) {
if(urlResponse.statusCode == 200) {
NSDictionary *response = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableLeaves error:&jsonError];
if(response) {
NSString *resp = [NSString stringWithFormat:#"%#", [dataResponse objectForKey:#"resp"]];
if([resp isEqualToString:#"1"]) {
_json = [dataResponse objectForKey:#"data"];
[_arrServiciosTmp addObjectsFromArray:(NSArray *)_json];
} else {
NSString *message = [dataResponse objectForKey:#"description"];
}
} else {
self.lblMensaje.text = #"Ha ocurrido un error al obtener la informaciĆ³n, por favor, vuelva a intentarlo en unos momentos.";
}
} else {
completion(nil);
}
} else {
NSLog(#"Error en Task");
}
});
And the function [self getListaProfesionalesPorLocal: [Local objectForKey: # "idLocal"]]; is similar but obtains other information
The problem is that the app calls this function [self setFormularioConsultaCompleto]; (changing the view) but the above functions still do not quite get all the data from webservice.
Sorry for this, but I gave up, I hope can help me!
Thanks!
The below uses dispatch groups to hold off starting another block till the groups work has been completed.
First change your data methods to not be wrapped in dispatch_async and accept a completion block, calling that at the end of the NSURLSessionDataTasks completionHandler:
-(void)getListaServiciosPorLocal:(id)whatEver withCompletionBlock:(dispatch_block_t)block
{
NSURL *url = [NSURL URLWithString:urlConnection];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration];
sessionConfiguration.timeoutIntervalForRequest = 30;
sessionConfiguration.timeoutIntervalForResource = 60;
NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfiguration];
NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
__block NSError *jsonError;
NSHTTPURLResponse *urlResponse = (NSHTTPURLResponse *) response;
if(!error) {
if(urlResponse.statusCode == 200) {
NSDictionary *response = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableLeaves error:&jsonError];
if(response) {
NSString *resp = [NSString stringWithFormat:#"%#", [dataResponse objectForKey:#"resp"]];
if([resp isEqualToString:#"1"]) {
_json = [dataResponse objectForKey:#"data"];
[_arrServiciosTmp addObjectsFromArray:(NSArray *)_json];
} else {
NSString *message = [dataResponse objectForKey:#"description"];
}
} else {
self.lblMensaje.text = #"Ha ocurrido un error al obtener la informaciĆ³n, por favor, vuelva a intentarlo en unos momentos.";
}
} else {
completion(nil);
}
} else {
NSLog(#"Error en Task");
}
block(); // Notify completion block
});
}
Now when you call these methods:
dispatch_group_t group = dispatch_group_create();
dispatch_async(queue, ^(void) {
NSLog(#"llego a buscar servicios por local");
for (NSDictionary *local in _arrLocalesTmp) {
dispatch_group_enter(group);
[self getListaServiciosPorLocal:[local objectForKey:#"idLocal"] withCompletionBlock:^{
dispatch_group_leave(group);
}];
}
NSLog(#"llego a buscar profesionales por local");
for (NSDictionary *local in _arrLocalesTmp) {
dispatch_group_enter(group);
[self getListaProfesionalesPorLocal:[local objectForKey:#"idLocal"] withCompletionBlock:^{
dispatch_group_leave(group);
}];
}
});
dispatch_group_notify(group, dispatch_get_main_queue(),^{
[self setFormularioConsultaCompleto];
});
(Adapted from this answer)

Resources