Make periodic server requests with NSURLSessionDataTask and dispatch_source timer - ios

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

Related

Max concurrent instances of a NSURLSessionDataTask

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.!!!

Why is background NSURLConnection having error when enter background?

I am having this error when my app enter background.
NSURLConnection finished with error - code -1001 Task
<09B84034-9F73-4DB6-A685-D891B1B1068A>.<2> finished with error - code:
-1001
I am using this code
- (id<XCDYouTubeOperation>) getVideoWithIdentifier:(NSString *)videoIdentifier completionHandler:(void (^)(XCDYouTubeVideo * __nullable video, NSError * __nullable error))completionHandler
{
NSLog(#"Getting Video Identfifier");
if (!completionHandler)
#throw [NSException exceptionWithName:NSInvalidArgumentException reason:#"The `completionHandler` argument must not be nil." userInfo:nil];
XCDYouTubeVideoOperation *operation = [[XCDYouTubeVideoOperation alloc] initWithVideoIdentifier:videoIdentifier languageIdentifier:self.languageIdentifier];
operation.completionBlock = ^{
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-retain-cycles"
if (operation.video || operation.error)
{
NSAssert(!(operation.video && operation.error), #"One of `video` or `error` must be nil.");
completionHandler(operation.video, operation.error);
}
else
{
NSAssert(operation.isCancelled, #"Both `video` and `error` can not be nil if the operation was not canceled.");
}
operation.completionBlock = nil;
#pragma clang diagnostic pop
}];
};
NSLog(#"Operation - %#", operation ) ;
[self.queue addOperation:operation];
return operation;
}`
- (void) startRequestWithURL:(NSURL *)url type:(XCDYouTubeRequestType)requestType
{
if (self.isCancelled)
return;
// Max (age-restricted VEVO) = 2×GetVideoInfo + 1×WatchPage + 1×EmbedPage + 1×JavaScriptPlayer + 1×GetVideoInfo
if (++self.requestCount > 6)
{
// This condition should never happen but the request flow is quite complex so better abort here than go into an infinite loop of requests
[self finishWithError];
return;
}
XCDYouTubeLogDebug(#"Starting request: %#", url);
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:10];
[request setValue:self.languageIdentifier forHTTPHeaderField:#"Accept-Language"];
NSLog(#"Request Type - ",requestType);
// NSURLSessionDataTask *task = [self.session dataTaskWithRequest:request];
// [task resume];
self.dataTask = [self.session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error)
{
if (self.isCancelled)
return;
if (error)
[self handleConnectionError:error];
else
[self handleConnectionSuccessWithData:data response:response requestType:requestType];
}];
[self.dataTask resume];
self.requestType = requestType;
}
#pragma mark - Response Dispatch
- (void) handleConnectionSuccessWithData:(NSData *)data response:(NSURLResponse *)response requestType:(XCDYouTubeRequestType)requestType
{
NSLog(#"XCDDRequestType - ",requestType);
CFStringEncoding encoding = CFStringConvertIANACharSetNameToEncoding((__bridge CFStringRef)response.textEncodingName ?: CFSTR(""));
// Use kCFStringEncodingMacRoman as fallback because it defines characters for every byte value and is ASCII compatible. See https://mikeash.com/pyblog/friday-qa-2010-02-19-character-encodings.html
NSString *responseString = CFBridgingRelease(CFStringCreateWithBytes(kCFAllocatorDefault, data.bytes, (CFIndex)data.length, encoding != kCFStringEncodingInvalidId ? encoding : kCFStringEncodingMacRoman, false)) ?: #"";
NSAssert(responseString.length > 0, #"Failed to decode response from %# (response.textEncodingName = %#, data.length = %#)", response.URL, response.textEncodingName, #(data.length));
XCDYouTubeLogVerbose(#"Response: %#\n%#", response, responseString);
switch (requestType)
{
case XCDYouTubeRequestTypeGetVideoInfo:
[self handleVideoInfoResponseWithInfo:XCDDictionaryWithQueryString(responseString) response:response];
break;
case XCDYouTubeRequestTypeWatchPage:
[self handleWebPageWithHTMLString:responseString];
break;
case XCDYouTubeRequestTypeEmbedPage:
[self handleEmbedWebPageWithHTMLString:responseString];
break;
case XCDYouTubeRequestTypeJavaScriptPlayer:
[self handleJavaScriptPlayerWithScript:responseString];
break;
}
}
This code will automatically run in background but after a few minutes it will stop and gives me the above error. How to fix this ?
EDIT 1 (Vinay Kiran Method) #
i changed the nsurlsessionconfiguration to background.
- (instancetype) initWithVideoIdentifier:(NSString *)videoIdentifier languageIdentifier:(NSString *)languageIdentifier
{
if (!(self = [super init]))
return nil; // LCOV_EXCL_LINE
_videoIdentifier = videoIdentifier ?: #"";
_languageIdentifier = languageIdentifier ?: #"en";
// _session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration ephemeralSessionConfiguration]];
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:#"YouTubeID"];
_session = [NSURLSession sessionWithConfiguration:configuration delegate:self delegateQueue:nil];
_operationStartSemaphore = dispatch_semaphore_create(0);
NSLog(#"Initialize the Video Identifier");
return self;
}
then change the completion handler since background it will give this error if i use handler
Swift: 'Completion handler blocks are not supported in background sessions. Use a delegate instead.'
- (void) startRequestWithURL:(NSURL *)url type:(XCDYouTubeRequestType)requestType
{
if (self.isCancelled)
return;
// Max (age-restricted VEVO) = 2×GetVideoInfo + 1×WatchPage + 1×EmbedPage + 1×JavaScriptPlayer + 1×GetVideoInfo
if (++self.requestCount > 6)
{
// This condition should never happen but the request flow is quite complex so better abort here than go into an infinite loop of requests
[self finishWithError];
return;
}
XCDYouTubeLogDebug(#"Starting request: %#", url);
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:10];
[request setValue:self.languageIdentifier forHTTPHeaderField:#"Accept-Language"];
NSLog(#"Request Type - ",requestType);
// NEWLY ADDED
NSURLSessionDataTask *task = [self.session dataTaskWithRequest:request];
[task resume];
// self.dataTask = [self.session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error)
// {
// if (self.isCancelled)
// return;
//
// if (error)
// [self handleConnectionError:error];
// else
// [self handleConnectionSuccessWithData:data response:response requestType:requestType];
// }];
// [self.dataTask resume];
self.requestType = requestType;
}
the problem now is that i originally use this
self.dataTask = [self.session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error)
{
if (self.isCancelled)
return;
if (error)
[self handleConnectionError:error];
else
[self handleConnectionSuccessWithData:data response:response requestType:requestType];
}];
[self.dataTask resume];
which handleConnectionSuccessWithData will take in data, response and request type. Now i don't know where can i get the data, response and request type if i use backgroundSessionConfigurationWithIdentifier.
Use background thread instead of the main queue
backgroundSessionConfigurationWithIdentifier:
For reference
https://developer.apple.com/documentation/foundation/nsurlsessionconfiguration/1407496-backgroundsessionconfigurationwi?language=objc

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.

Activity Indicator while calling API

I want to display the activity indicator while waiting for the API to return. The problem is after all the result I get from API, then the spinner only display. The result I want is while waiting for API call, then the spinner will running.
I'm calling this method in here
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
[self startLoadingSpinner]
//Calling API...
[self stopLoadingSpinner]
}
Here is the method for the activity indicator
-(void)startLoadingSpinner {
self.activityIndicator = [[UIActivityIndicatorView alloc] initWithFrame:CGRectMake(0, 0, 70, 70)];
self.activityIndicator.opaque = YES;
self.activityIndicator.backgroundColor = [UIColor colorWithWhite:0.0f alpha:0.4f];
self.activityIndicator.center = self.view.center;
self.activityIndicator.activityIndicatorViewStyle = UIActivityIndicatorViewStyleGray;
[self.activityIndicator setColor:[UIColor whiteColor]];
[self.view addSubview:self.activityIndicator];
[self.activityIndicator startAnimating];
}
This is how I stop the activity indicator
-(void)stopLoadingSpinner {
[self.activityIndicator performSelector:#selector(removeFromSuperview) withObject:nil afterDelay:0.5];
}
Don't add activity indicators in tableview datasource method - numberOfRowsInSection .
Add these two functions calling in the same method where you are making an API call. Make an API call in ViewDidLoad, some life cycle method or in action methods.
Below is the example of using it.
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:configuration];
NSURL *URL = [NSURL URLWithString:#"http://httpbin.org/get"];
NSURLRequest *request = [NSURLRequest requestWithURL:URL];
[self startLoadingSpinner]
NSURLSessionDataTask *dataTask = [manager dataTaskWithRequest:request completionHandler:^(NSURLResponse *response, id responseObject, NSError *error) {
if (error) {
NSLog(#"Error: %#", error);
} else {
NSLog(#"%# %#", response, responseObject);
}
[self stopLoadingSpinner]
}];
[dataTask resume];
In Swift
func makeAPIRequest(to endPoint: String) {
// here you can showActivetyIndicator start progressing here
self.startLoadingSpinner()
Alamofire.request(endPoint).responseJSON{ response in
if let value = response.result.value {
let responseInJSON = JSON(value)
self._responseInJSON = responseInJSON
}
// here you can hide Your ActivetyIndicator here
self.stopLoadingSpinner()
}
}
My detailed answer is below
-(void)simpleGetResponse{
#try {
//Call the Activity Indicator show method here
[self startLoadingSpinner];
NSString *strURL = #"Your URL";
NSURL *urlStr = [NSURL URLWithString:strURL];
NSMutableURLRequest *mutaURL = [NSMutableURLRequest requestWithURL:urlStr];
[mutaURL setHTTPMethod:#"GET"];
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:mutaURL completionHandler:^(NSData *data, NSURLResponse *response, NSError *error)
{
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
if(httpResponse.statusCode == 200)
{
NSError *parseError = nil;
id response = [NSJSONSerialization JSONObjectWithData:data options:0 error:&parseError];
if(response != nil){
if([response isKindOfClass:[NSDictionary class]]){
NSLog(#"response is in dictionary format %#",response);
NSDictionary *dictRes = [response copy];
NSLog(#"The dictRes is - %#",dictRes);
}
else{
NSLog(#"response is in array format %#",response);
NSDictionary *arrRes = [response copy];
NSLog(#"The arrRes is - %#",arrRes);
}
dispatch_async(dispatch_get_main_queue(), ^{
//Call the Activity Indicator hidden method inside the dispatch_main_queue method
[self stopLoadingSpinner]
[yourTableView reloadData];
});
}
}
else
{
NSLog(#"Error");
}
}];
[dataTask resume];
}
#catch (NSException *exception) {
NSLog(#"%#", [exception description]);
}
#finally {
}
}

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