wait for request and then tableview call - objective-c

I am using a ASIHTTPRequest to get a value from the url. this process is done in view did load. and I get value from a url store in an array. I have one table view. The problem is when I run the app table view is first called and it take array value is 0. my request is taking some time for loading. how to solve it.i am new to us. help me. i want to lode that array in to my tableview.
May I know what is the correct way to achieve my objective?
NSString *s= [NSString stringWithFormat:#"http:///blog/api/get_category_posts/?id=%#",value];
s=[s stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSURL *url1=[NSURL URLWithString:s];
ASIHTTPRequest *req2=[[ASIHTTPRequest alloc]initWithURL:url1];
[req2 setDelegate:self];
[req2 startAsynchronous];}-(void)requestFinished:(ASIHTTPRequest *)request
{
tableResourceArray=[request.responseString JSONValue];
NSLog(#"TempArray:%d",tableResourceArray.count);} -(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return tableResourceArray.count;
}

just reload table after get response.
in your case
[req2 startAsynchronous];}
-(void)requestFinished:(ASIHTTPRequest *)request
{
tableResourceArray=[request.responseString JSONValue];
[tableName reloadData]; // ** that's you need**
NSLog(#"TempArray:%d",tableResourceArray.count);
}
-(NSInteger)tableView: (UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return tableResourceArray.count;
}

Related

xcode Core Data edit value for key issue

Got a problem here...
My BOOL gets edited and I get success at the last NSLog, but when I close the ViewController and then go in again (update the table), the BOOL go back to the first value. That will say - something is wrong in my [context save:&error]; function.
Any ideas?
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
for (int i=0; i<[self tableView:tableView numberOfRowsInSection:0]; i++) {
AccountCell *cell = (AccountCell *)[tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:i inSection:0]];
[cell setSelected:(i==indexPath.row) animated:NO];
NSManagedObject *user = [arr objectAtIndex:indexPath.row];
[user setValue:[NSNumber numberWithBool:(i==indexPath.row)] forKey:#"active"];
NSLog(#"Index: %i, Active State: %#", i,[user valueForKey:#"active"]);
NSError *error;
if (![context save:&error]) {
NSLog(#"Saving changes to context failed: %#", error);
} else {
// The changes have been persisted.
NSLog(#"Saved data success");
}
}
}
Some suggestions:
It would make much more sense to put the save statement outside the for loop.
You need to check if
your managed object context is valid (non-nil)
the context of the objects of your mysterious arr array is the same as the context you are saving
the "active" property (including spelling) is correctly configured in your model and the managed object (maybe you want to subclass for more clarity rather than relying on KVC).
there is something in the error variable
I also think there are some other design flaws. For example, you are getting cells and setting their selected state even though they might not even be visible. IMO, you should do this in cellForRowAtIndexPath, based on the state of the underlying managed object.
As for deselecting all other users in the same section you are right that a loop is probably inevitable. But I suppose it would be more efficient to fetch all users in a section at once and then loop through them to set the "active" property as desired.

AFNetworking Data Fetching Method Not Called

I am creating an app where in the first view, the user is given the option to log-in or register. In the register view is a UITableViewCell that, when clicked, takes the user to a view containing a UITableView and a UIPickerView. The UITableView is working correctly, but the UIPickerView, which is supposed to dynamically pull the data it is supposed to display using a web call, is showing up but appears completely blank. Putting in a few NSLog statements, I noticed that the methods in the Model that pull the data using AFNetworking are never getting called. I've posted the code below for the UIPickerViewDelegate and UIPickerViewDataSource methods, as well as the method that is supposed to pull the data in the Model. Thanks in advance.
UIPickerViewDelegate
- (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row
forComponent:(NSInteger)component {
return [[self.brain classChoicesForSignUp] objectAtIndex:row];
}
UIPickerViewDataSource
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView {
return 1;
}
- (NSInteger)pickerView:(UIPickerView *)pickerView
numberOfRowsInComponent:(NSInteger)component {
size_t numberOfRows = [self.brain classChoicesForSignUp].count;
NSLog(#"Number of Rows: %#", [[NSNumber numberWithFloat:numberOfRows] stringValue]);
return numberOfRows;
}
SignUpPickerBrain.m
#import "SignUpPickerBrain.h"
#import "AFJSONRequestOperation.h"
#implementation SignUpPickerBrain
#pragma mark - Picker Data
- (NSArray *)classChoicesForSignUp {
NSLog(#"Class choices method called");
// Note that in my code, the actual URL is present here.
NSURL *url = [NSURL URLWithString:#"the URL"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) {
NSLog(#"Success!");
NSLog([JSON description]);
} failure:nil];
[operation start];
[operation waitUntilFinished];
NSLog([operation responseJSON]);
return [operation responseJSON];
}
#end
There are a lot of anti-patterns going on in this code sample. I strongly recommend against your current approach, and consider the following points:
Do networking asynchronously, i.e. don't use [operation waitUntilFinished];. Any time you're creating a method that makes a network request, give it a block parameter that can be used as a callback once the results come in.
Store your results in an array property in the controller, or the like, and use that to drive your delegates and datasources. In your current approach, you will be doing a network request every single time a row is displayed (!). So instead, initialize to an empty array, and once the new results are set to that property, reload the data source. One asynchronous request. Easy.
Get rid of SignUpPickerBrain. Either use a proper Model, or just make the call itself in the Controller. The example iOS project has some great patterns to follow.
Use AFHTTPClient. If you're interacting with a particular webservice, it can be very useful to have an AFHTTPClient subclass to handle all of those requests.

How Can I Pass Data from Array in didLoadObjects to cellForRowAtIndexPath

So, I'm using RestKit to access a webservice and retrive data from there.
So far I have two views, and that part of retrieving the data is fine, and I get the 100 objects I need, saving them into an array called "songs".
Here is didLoadObjects:
- (void)objectLoader:(RKObjectLoader*)objectLoader didLoadObjects:(NSArray*)objects {
NSLog(#" Reached didLoadObjects: %d", [objects count]);
self.songs = objects;
NSLog(#"%#",self.songs);
}
Ok, I have two views, and the problem is, of course, on the second one. I'm using Storyboards. I gave the tableView cell an identifier called "TopListCellIdentifier".
So, I get the objects, all the 100 of them are "printed" in the command line, but the problem starts when I try to access the data from the array inside cellForRowAtIndexPath, something which has to be done because I have a custom tableViewCell displaying the info I need (sons, artists, covers, stuff like that). So, when I start the app, the first view is fine, but the second has 100 cells, but no info. Here is the cellForRowAtIndexPath code.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *TopListCellIdentifier = #"TopListCellIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:TopListCellIdentifier];
// Loading the Cover Images in the Background
// Cover Image: Tag 1
[((HJManagedImageV*)[cell viewWithTag:1]) clear];
HJManagedImageV* thumbImage = ((HJManagedImageV*)[cell viewWithTag:1]);
NSString *thumbUrl = [[self.songs objectAtIndex:[indexPath row]] thumbnail];
thumbImage.url = [NSURL URLWithString:thumbUrl];
[[ImageHandler sharedHandler].imgManager manage:thumbImage];
//Song and Artist: Tag 2 and 3
((UILabel*)[cell viewWithTag:2]).text = [[self.songs objectAtIndex:[indexPath row]] title];
((UILabel*)[cell viewWithTag:3]).text = [[self.songs objectAtIndex:[indexPath row]] artist];
//Arrow Up/Down/Same: Tag 4
//TODO
//Position Number: Tag 5
((UILabel*)[cell viewWithTag:5]).text = [NSString stringWithFormat:#"%d.", [indexPath row]+1];
return cell;
}
I've tried to put a debugger in the first line of cellRow(...) but the program doesn't enter there. I feel like i'm forgetting about something very simple, but I can't seem to figure out what.
Can someone help me, please?
You're never initing a new cell. You need to add a line like :
if (nil == cell)
cell = [[UITalbeViewCell alloc] init...
after your call to
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:TopListCellIdentifier];

NSRangeException when using Core Data Asynchronous UISearchDisplayController

I'm asynchronously fetching data, and I've used this as a guide: http://deeperdesign.wordpress.com/2011/05/30/cancellable-asynchronous-searching-with-uisearchdisplaycontroller/
In
- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString{
//setup request / predicate etc...
[self.searchQueue addOperationWithBlock:^{
NSError *error;
self.matchingObjects = [self.managedObjectContext executeFetchRequest:request error:&error];
[request release];
[[NSOperationQueue mainQueue] addOperationWithBlock:^
{
[self.searchDisplayController.searchResultsTableView reloadData];
}];
}];
// Return YES to cause the search result table view to be reloaded.
return NO;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
// Return the number of rows in the section.
return [self.matchingObjects count];
}
Every now and then I'll get something to the effect of:
*** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[NSArray objectAtIndex:]: index 0 beyond bounds for empty array'
This is thrown at the matchingObjects ivar when accessing it to construct the table cell in:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
The crash doesn't occur all the time, just seems to happen on random occasions. I'm guessing that somewhere the count on the matchingObjects array is returning a certain value, which changes and is not being updated.
I'm not entirely sure of how to deal with this - been looking over this for hours, is there something I'm missing?
I figured out what it was - took me a while, but I looked again at the example that I just linked. I was updating the self.matchingObjects iVar in the background thread, which on some occasions caused a mismatch between the range of the array available in the main thread and the background thread. So for example, the variable may have been updated in the background thread, and the main thread may still be accessing a part of the range that no longer exists in the variable since it was updated.
Fixed it by amending my code as follows:
[self.searchQueue addOperationWithBlock:^
{
NSError *error;
NSArray *results = [self.managedObjectContext executeFetchRequest:request error:&error];
[request release];
[[NSOperationQueue mainQueue] addOperationWithBlock:^
{
self.matchingObjects = results;
[self.searchDisplayController.searchResultsTableView reloadData];
}];
}];
Now the results of the search are loaded into a temporary holding array named "results", and the matchingObjects iVar is first updated in the main thread and then the tableView is reloaded. This way, the tableView always is referring to an array that is never changed whilst it is being accessed, since the tableView relies on matchingObjects to get the number of rows and data.

Trouble with Asynchronous Downloading UITableView

I am trying to asynchronously download images for a UITableViewCell, but it is currently setting the same image to each cell.
Please can you tell me the problem with my code:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease];
}
SearchObject *so = (SearchObject *)[_tableData objectAtIndex:indexPath.row];
cell.textLabel.text = [[[[so tweet] stringByReplacingOccurrencesOfString:#""" withString:#"\""] stringByReplacingOccurrencesOfString:#"<" withString:#"<"] stringByReplacingOccurrencesOfString:#">" withString:#">"];
cell.detailTextLabel.text = [so fromUser];
if (cell.imageView.image == nil) {
NSURLRequest *req = [NSURLRequest requestWithURL:[NSURL URLWithString:[so userProfileImageURL]]];
NSURLConnection *conn = [NSURLConnection connectionWithRequest:req delegate:self];
[conn start];
}
if ([_cellImages count] > indexPath.row) {
cell.imageView.image = [UIImage imageWithData:[_cellImages objectAtIndex:indexPath.row]];
}
return cell;
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[_cellData appendData:data];
[_cellImages addObject:_cellData];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
[self.tableView reloadData];
}
You are appending the data from every image downloaded to the same data object. So in the best case the data object ends up with the data for image #1 immediately followed by the data for image #2 and so on; the image decoder is apparently taking the first image in the chunk of data and ignoring the garbage after. You also seem to be unaware that NSURLConnections' connection:didReceiveData: will not necessarily be called in the order that the connections were started, that connection:didReceiveData: can be called zero or multiple times per connection (and probably will if your images are more than a few kibibytes), and that tableView:cellForRowAtIndexPath: is not guaranteed to be called for every cell in the table in order. All of which are going to totally screw up your _cellImages array.
To do this right, you need to have a separate NSMutableData instance for each connection, and you need to add it to your _cellImages array just once, and at the correct index for the row rather than at the arbitrary next available index. And then in connection:didReceiveData: you need to figure out the correct NSMutableData instance to append to; this could be done by using the connection object (wrapped in an NSValue using valueWithNonretainedObject:) as the key in an NSMutableDictionary, or using objc_setAssociatedObject to attach the data object to the connection object, or by making yourself a class that handles all the management of the NSURLConnection for you and hands you the data object when complete.
I don't know if this is causing the problem or not, but in your connection:didReceiveData: method you're just appending the image data to the array; you should be storing the image data in such a way that you can link it to the cell it's supposed to be shown in. One way to do this would be use an NSMutableArray populated with a bunch of [NSNull]s, then replace the null value at the appropriate index when the connection has finished loading.
Also, you're appending the _cellData to the _cellImages array when the connection hasn't finished loading, you should only be doing this in the connection:didFinishLoading method.