How to perform multi AsynchronousRequests in ios - ios

i have a UITableView which shows (doctor image , doctor Name)
In the cellForRowAtIndexPath function iam fetching the images as follow:
[NSURLConnection sendAsynchronousRequest:request
queue:[NSOperationQueue mainQueue]
completionHandler:^(NSURLResponse * response,
NSData * data,
NSError * error) {
if (!error){
dispatch_async(dispatch_get_main_queue(), ^{
updateCell.DRImage.image = [[UIImage alloc] initWithData:data];
}];
The Problem is: when i'm scrolling down fast , the images start loading , and almost 100 images being loaded , then when i click on row to show the doctor details , i can't get the data from the server until all the images finish loading (until all async requests finish).
i'm using the following code to get the doctor details:
SoapCall *Obj = [[SoapCall alloc] init];
NSString* Url=[[NSString alloc] initWithFormat:#"%#/doctorsDetails.php?i=%#" , ServerUrl ,DoctorDataFromTableView.Doctor_ID];
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
dispatch_async(queue, ^{
[Obj CallUrl:Url OnCompletion:^(NSMutableArray * Results , NSError * Error){
dispatch_async(dispatch_get_main_queue(), ^{
Nviews.text =[[Results objectAtIndex:0] objectForKey:#"numViews"];
NComments.text =[[Results objectAtIndex:0] objectForKey:#"totComments"];
});
}];
Note: SoapCall is a custom class i made to make an Async request to the server
my question is :
Is there is any way to cancel loading the images???
Or can i load the Doctor details in different thread ???

i found a great project which is "SDWebImage"
https://github.com/rs/SDWebImage
it's using NSoperationQueue and NSOperation in a very advanced way
This project saved a lot of time for me

Related

iOS app crash recover method

Since 2 yrs I have been trying different ways to find the solution of app crash while click back button.
My application scenario:
In a tableview contoller I have to load list of users, On view did load I call getData(Asyncronous download) API method to load data. At the time of data download, If user press back button my application gets crash due to null value objects. That says all of my variable memory deallocated.
To overcome this problem, I used some loading indicator which lock UIScreen untill data download.
Questions:
Is there any alternatives to prevent crash, UIScreen Lock
Other applications use Activity Indicator in Menu bar without UIScreen Lock. How they are doing?
Need help to recover this issue
Here is my sample code to download data :
Below code doesnt crash app. But it download data even I cancel operations on dealloc
viewDidLoad:
ShowNetworkActivityIndicator();
_processQueue = [[NSOperationQueue alloc] init];
_processQueue.maxConcurrentOperationCount = 4;
_processQueue.name = #"Events Processing";
[self loadData];
loadData:
-(void)loadData
{
[_processQueue addOperationWithBlock: ^ {
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
[request setURL:[[NSURL alloc] initWithString:#"https://restcountries.eu/rest/v1/all"]];
NSURLResponse *response;
NSData *urlData=[NSURLConnection sendSynchronousRequest:request returningResponse:&response error:nil];
NSString *data=[[NSString alloc]initWithData:urlData encoding:NSUTF8StringEncoding];
NSDictionary *search = [NSJSONSerialization JSONObjectWithData:[data dataUsingEncoding:NSUTF8StringEncoding] options:NSJSONReadingMutableContainers error:nil];
[[NSOperationQueue mainQueue] addOperationWithBlock: ^ {
_countryListArray=[search mutableCopy];
[self.tableViewSample reloadData];
HideNetworkActivityIndicator();
}];
}];
}
I tried cancelAllOperations in dealloc:
[_processQueue setSuspended:YES];
[_processQueue cancelAllOperations];
Can you try inserting the reload data is dispatch_async(dispatch_get_main(),void (^){}); callback , main thread , I think the reload happening in the background thread is crashing the app.
[[NSOperationQueue mainQueue] addOperationWithBlock: ^ { _countryListArray=[search mutableCopy]; dispatch_async(dispatch_get_main_queue(), ^{
[self.tableViewSample reloadData];
HideNetworkActivityIndicator();
});}];

How to load JSON data asynchronously and populate UITableView during the data loading in objective-C

I am beginner in iOS and objective C programming, and I have an issue I didn't manage to solve because I just don't know where to begin, and what to do. Here is the context : I have a sidebar menu (like facebook's one), and when you select a category of this sidebar menu, I display an UITableView that contents a list of image and some text, the list of image and the text comes from internet with a JSON feed. My app currently works fine, but each time I change the category it freezes the time to load JSON data from internet. Here is a sample of code of my TableViewController.m :
- (void)viewDidLoad {
[super viewDidLoad];
[self refreshTable];
}
-(void)refreshTable {
// Send a synchronous request
NSURLRequest * urlRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:URLValue]];
NSURLResponse * response = nil;
NSError * error = nil;
NSData * data = [NSURLConnection sendSynchronousRequest:urlRequest
returningResponse:&response
error:&error];
if (error == nil)
{
// Parse JSON data
}
}
I would really appreciate some explanations with the steps to follow since I am really beginner. I have found some similar questions and they talk about delegate, what is delegate and how do I call it?
Thank you very much guys for taking the time to read my issue.
You want to use:
[NSURLConnection sendAsynchronousRequest:urlRequest queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
// parsing code goes here
// update table view
[self.tableView reloadData];
}
Move your parsing code inside the completion block. For more info see: https://developer.apple.com/library/Mac/documentation/Cocoa/Reference/Foundation/Classes/NSURLConnection_Class/Reference/Reference.html#//apple_ref/occ/clm/NSURLConnection/sendAsynchronousRequest:queue:completionHandler:.
At the end of parsing you can call [self.tableView reloadData] to refresh.

UITableView - AFNetworking operation in background freezes scrolling

I have a sample application with a UITableViewController.
As in the facebook newsfeed, the app is supposed to download a first time X news, and then fetch news progressively as the user scroll.
Here is my implementation :
-(void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath{
if (indexPath.row == self.newsList.count-PADDLE_BEFORE_FETCHING && !cantFetchMore)
if (!fetching){
fetching = YES;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
[self fetchNews];
});
}
}
(the idea is to start fetching additional news when we reach the N-PADDLE_BEFORE_FETCHING cell, only if we can still fetch some - see below- and if fetching is still not currently running)
and then the implementation of fetchNews :
-(void)fetchNews{
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSString *url = [NSString stringWithFormat:#"%#%#%#%#%d%#",HOSTNAME,GET_NEWS,[defaults objectForKey:#"oAuthToken"],#"&limit=",FETCH_SIZE_NEWS,[NSString stringWithFormat:#"&offset=%d",self.newsList.count]];
NSURLRequest *request =[[NSURLRequest alloc] initWithURL:[NSURL URLWithString:url]];
AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) {
#if DEVELOPMENT_MODE
NSLog(#"News : %#",JSON);
NSLog(#"Response : %#\n Request : %#",response,request);
#endif
//NSLog(#"Number of news fetched : %d",((NSArray*)JSON[#"data"]).count);
for (NSDictionary *d in JSON[#"data"]){
News *new = [[News alloc] initWithDictionary:d];
[self.newsList addObject:new];
new = nil;
}
if ((((NSArray*)JSON[#"data"]).count)%FETCH_SIZE_NEWS !=0) cantFetchMore = YES;
//NSLog(#"%d cantFetch",cantFetchMore);
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
[self.tableView reloadData];
fetching = NO;
} failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON) {
NSLog(#"Request error : %# %# %#",request,error, JSON);
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
fetching = NO;
}];
[operation start];
}
This will fetch FETCH_SIZE_NEWS additional news from the server starting at the good offset which is the current size of the newsList array.
Also, if the count of fetched news % FETCH_SIZE_NEWS is different from 0, that means that we cannot fetch additional news (which will prevent from calling the webservice while scrolling the UITableView).
My issue is that when the fetching is done (exactly when I see the activity wheel running in the status bar), it blocks the GUI, and I cant continue to scroll down from the n-PADDLE_BEFORE_FETCHING cells to the n cells, or even scroll up to the previously loaded cells.
I don't really understand why as AFNetworking is supposed to run asynchronously.
Any ideas?
Thanks,
The for-loop in the completion block is running on the main thread and may be causing the slow-down. Try sending that code to another thread/queue.
As stated above buy Guy Kogus, the processing within the complete block of the AFNetworking operation was adding a bit a freezer on the main thread while scrolling.
Just added
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{//processing block}
Within the complete block (notably the foreach loop) and this is far better.
Thanks

Unresponsive TableView while WebRequest (AFNetworking) on device, but ok in simulator + MagicalRecord issue

I have an application that retrieves json (employees workschedules) from a web service using AFNetworking and displays them in a table view.
I have my webservice class that takes care of doing the request and once it is done, it stores these data into coredata (I have an another issue here, being that I use magicalRecord and the data does not persist, and I don't understand why) and then calls back its delegate (my tableViewController) telling it it's done, so this can load the workschedules into the cells.
WebServiceClient.m
NSURL *url = [NSURL URLWithString:stringUrl];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request
success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON)
{
NSArray *workSchedules = [[[NSSet alloc] initWithArray:JSON] allObjects];
NSManagedObjectContext *context = [NSManagedObjectContext MR_contextForCurrentThread];
Workschedule *workscheduleEntity = nil;
NSError *error = nil;
for (NSDictionary *web_workschedule in workSchedules)
{//Inside this method I create other entities that will hydrate my workschedule entity, and it is done using the MR_CreateInContext
workscheduleEntity = [Workschedule workScheduleFromJSONDictionary:web_workschedule withError:&error];
[context MR_save];
}
if([self.delegate respondsToSelector:#selector(workSchedules)]){
[self.delegate workSchedules];
}
}
failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON) {
LOG_ERROR(2,#"Received an HTTTP %d:", response.statusCode);
LOG_ERROR(2,#"The error was: %#", error);
if([self.delegate respondsToSelector:#selector(workSchedules:)]){
[self.delegate workSchedules:nil];//return error
}}];
NSOperationQueue *operationQueue = [[NSOperationQueue alloc] init];
[operationQueue addOperation:operation];
}
PendingWorkscheduleViewController.m
- (void)viewDidLoad
{
[super viewDidLoad];
[self.webServiceClient getMockedWorkSchedulesForEmployee:[NSNumber numberWithInt:1]];
[self workSchedules];
}
-(void)workSchedules
{
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"pending == YES"];
NSArray *pendingWorkSchedules = [Workschedule MR_findAllWithPredicate:predicate];
self.pendingWorkSchedules = pendingWorkSchedules;
[self.tableView reloadData];
}
My problem is that when i run this while the request is processed the UI is unresponsive (it's a very brief time, but if the request were to increase...) so that if i load the table view and right away try to scroll or click the back button, it just ignores it as it is "frozen". This behavior is on my iphone 4s. On the simulator this works fine and I can't wrap my head around why is that. I tried to call the "[self.webServiceClient getMockedWorkSchedulesForEmployee:[NSNumber numberWithInt:1]];" in a queue using GCD, I tried using performSelectorInBackground: WithObject: etc but still the same (even though with this last method it seemed a little more efficient, but it's an impression and only on the simulator, no changes on the device).
As far as magicalRecord goes I will make separate question.
I would appreciate your help.
Fixed it. The problem is that the success block run on the main thread! (which I did not understand). I just used GCD in the success block with a background queue for processing the data and the main queue to store this data in core data.
As far as magical record issue, i needed to save "nestedContext".
Cheers everyone.

Using NSLog in Unit Test, the output is too slow, only few times are fast

I'm facing a strange problem with using NSLog in iOS's unit test.
The Code
Here is the test code I'm using in my unit test.
- (void)testExample
{
NSOperationQueue *newqueue = [[NSOperationQueue alloc] init];
NSURLRequest *request2 = [[NSURLRequest alloc] initWithURL:[NSURL URLWithString:#"http://www.apple.com"]];
dispatch_semaphore_t sema2 = dispatch_semaphore_create(0);
[NSURLConnection sendAsynchronousRequest:request2 queue:newqueue completionHandler:^(NSURLResponse *resp, NSData *data, NSError *error) {
// NSLog(#"2: Data: %#", data);
printf("2: Data: %s\n", [[data description] UTF8String]);
dispatch_semaphore_signal(sema2);
}];
// sleep(2);
dispatch_semaphore_wait(sema2, DISPATCH_TIME_FOREVER);
dispatch_release(sema2);
NSURLRequest *request = [[NSURLRequest alloc] initWithURL:[NSURL URLWithString:#"http://www.google.com"]];
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
[NSURLConnection sendAsynchronousRequest:request queue:queue completionHandler:^(NSURLResponse *resp, NSData *data, NSError *error) {
NSLog(#"1: Data: %#", data);
dispatch_semaphore_signal(sema);
}];
// sleep(2);
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
dispatch_release(sema);
NSLog(#"Over");
}
To use the code
Create a simple iOS project with Unit Test.
Paste the code above to the xxxTest.m
Press ⌘ + u
Patterns I tried
Change the order of 2 different urls, for example let www.google.com run at first or let www.apple.com run at first.
Change the use of dispatch_semaphore_xxx to sleep().
Change NSLog() to printf()
Run this code as a Cocoa console app.
Remove the test of second URL request.
Results
Running time 0.4s ~ 30s
Running time 0.4s ~ 30s
Running time 0.4s ~ 30s
Running time ~0.4s
Running time ~0.4s
Questions
What is happening when I try the first 3 patterns?
Do you have a similar problem with you?
Are there any solutions to these?
Thank you guys!
This is an issue with SenTest and NSLog. Basically, the logs are piped to the test runner, which sends the data back to the console window in XCode.
NSLog writes to stderr, so if you redirect that to a file, you can get your log data.
Unfortunately, it still takes a while to write it all back out, when everything is done.
If I need to log a bunch of data during a test, I'll redirect stderr to a file, then "tail -f" it while the test is running if I want to watch it scroll. When it's over, I can then view the file in mvim or XCode.

Resources