Loading JSON Asynchronously on iPhone in UITableView - iphone

I am trying to load JSON from a sever and then display it in a UITableView. The request goes fine however when I try to add the data to the tableview the app crashes when I call then [tableView reloadData] method is called. I have the variable jsonArray reference in the UITableView methods so that the TableView displays the contents of that variable. Is this the best way to do it and what am I doing wrong?
The connection
NSURLConnection *theConnection=[[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
Main HTTP Callback
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
[connection release];
NSString *responseString = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
[responseData release];
//Declare the parser
SBJsonParser *parser = [[SBJsonParser alloc] init];
jsonArray = [parser objectWithString:responseString];
[tableView reloadData];
[parser release];
[responseString release];
}
Error
2011-07-07 14:42:56.923 Viiad[16370:207] -[__NSSet0 objectAtIndex:]: unrecognized selector sent to instance 0x5a4a0b0
2011-07-07 14:42:56.925 Viiad[16370:207] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSSet0 objectAtIndex:]: unrecognized selector sent to instance 0x5a4a0b0'
EDIT
UITableViewMethods
// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
NSArray *results = [[jsonArray valueForKey:#"Rows"] valueForKey:#"name"];
cell.textLabel.text = (NSString *)[results objectAtIndex:indexPath.row];
return cell;
}

This message
-[__NSSet0 objectAtIndex:]: unrecognized selector sent to instance 0x5a4a0b0
means that someplace in the code, the objectAtIndex: message was sent to an instance of __NSSet0. To me, that looks like you're passing around an NSSet where you're expecting an NSArray.
Set a debugger breakpoint before you call [results objectAtIndex:indexPath.row] and see what results actually contains. My guess is that the JSON parser isn't returning what you think it is. Objective-C is dynamically typed: just because you say that a variable is an NSArray type doesn't mean it can't contain some other object. If a function returns type id, there is no type information for the compiler to check at all.

Related

Filling tableview from NSDictionary

I am trying to fill the cells of a dynamic tableview with a NSDictionary I believe, here is my method to fill the tableview:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
ResultsCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
NSData *jsonData = self.responseFromServer;
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:jsonData options:kNilOptions error:nil];
NSArray *results = [json objectForKey:#"myData"];
for (NSDictionary *item in results) {
cell.title.text =[[item objectForKey:#"title"]objectAtIndex:indexPath.row];
}
// Configure the cell...
return cell;
}
If I just have
cell.title.text =[item objectForKey:#"title"];
almost works but all my titles are the same. But how it is currently I get the error:
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSCFString objectAtIndex:]: unrecognized selector sent to instance 0x7612940'
and im not sure what it means or how to fix it.
It seems like your Dictionary is actually an array of Dictionaries each with the key #"Title".
What you are doing right now is getting the String of each element and trying to get the index of indexPath.row, but strings do not have that method.
Since you only need the object at index indexPath.row, you can replace the whole for loop with the following line of code:
cell.title.text = [[results objectAtIndex:indexPath.row] objectForKey:#"title"];
Also, as Nicholas Hart said, in order to improve performance by a lot you should put the following lines in the code when you receive the json object so that it is only done once, and make results an instance variable that can be accessed from the tableView's delegate methods:
NSData *jsonData = self.responseFromServer;
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:jsonData options:kNilOptions error:nil];
NSArray *results = [json objectForKey:#"myData"];

JSON Object to NSArray

I have to following JSON response from a server:
{
"theSet": [
],
"Identifikation": 1,
"Name": "Casper",
"Adress": "Lovis 23",
"Location": "At home",
"Information": "The first, the second and the third",
"Thumbnail": "none",
}
I am retrieving the data like so:
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
NSLog(#"connectionDidFinishLoading");
NSLog(#"Succeeded! Received %d bytes of data",[data length]);
NSError *myError = nil;
NSDictionary *res = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableLeaves error:&myError];
news = [NSArray arrayWithObjects:[res allKeys], [res allValues], nil];
NSLog(#"%#", news);
//[mainTableView reloadData];
}
Then I want to insert all the JSON data into an array, so I can display the data in my tableview.
My tableview code:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"MainCell"];
if(cell == nil){
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:#"MainCell"];
}
cell.textLabel.text = [[news objectAtIndex:indexPath.row] objectForKey:#"Name"];
cell.detailTextLabel.text = [[news objectAtIndex:indexPath.row] objectForKey:#"Adress"];
return cell;
}
But my app crashes with the error:
-[__NSArrayI objectForKey:]: unrecognized selector sent to instance.
How can I insert the JSON object into an NSArray, so I can display it in my tableview?
EDITED:
I reviewed your code again and what I previously answered was wrong.
When generating your news, you are putting 2 NSArray objects in it. The first containing all keys, and the second containing all values in your JSON.
In order to display the names of each object in your JSON, you should be simply doing
news = [res allKeys];
jsonResult = res;
// store your json if you want to use the values!
Note they will be unordered.
On your cell, you can do:
NSString *key = [news objectAtIndex:indexPath.row];
cell.textLabel.text = key;
id object = [jsonResult valueForKey:key];
cell.detailTextLabel.text = // do something depending on your json type which can have different values
The error you have
[__NSArrayI objectForKey:]: unrecognized selector sent to instance.
Tells you everything you need to know.
This says NSArray does not understand objectForKey. If you read the documentation provided by Apple, you will see this.
Your code
cell.textLabel.text = [[news objectAtIndex:indexPath.row] objectForKey:#"Name"];
Is expecting the news NSArray to return an object that responds to objectForKey - most likely an NSDictionary. The code you have for extracting your JSon data
NSDictionary *res = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableLeaves error:&myError];
news = [NSArray arrayWithObjects:[res allKeys], [res allValues], nil];
Is just taking all of the dictionaries and extracting the keys into the array.
You need to look at these lines of your code - this is where you are going wrong.
Look at the NSJSONSerialization reference and the releated sample code that it links to.
I found a simple solution myself, that fits my project better:
I just did the following:
NSDictionary *res = [NSJSONSerialization JSONObjectWithData:data options:0 error:&myError];
NSArray news = [NSArray arrayWithObject:res];
and with that I am able to use the following code to display the JSON contents in my tableview.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"MainCell"];
if(cell == nil){
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:#"MainCell"];
}
cell.textLabel.text = [[news objectAtIndex:indexPath.row] valueForKey:#"name"];
cell.detailTextLabel.text = [[news objectAtIndex:indexPath.row] objectForKey:#"Location"];
return cell;
}

'NSInvalidArgumentException' testViewController tableView:numberOfRowsInSection

I trying to bind the Json parse data with table view using below code and getting exception. I am using ASIHttpRequest and Json as well.
**Error details:**
webservice[2864:f803]
***
Terminating app due to uncaught exception 'NSInvalidArgumentException',
reason: '-[testViewController tableView:numberOfRowsInSection:]:
unrecognized selector sent to instance 0x6ecc4e0'
***
NSString *responseString = [request responseString];
SBJsonParser *parser=[[SBJsonParser alloc]init];
NSDictionary *obj=[[NSDictionary alloc]init];
obj=[parser objectWithString:responseString error:nil];
return obj.count;
NSString *responseString = [request responseString];
SBJsonParser *parser=[[SBJsonParser alloc]init];
NSDictionary *obj=[[NSDictionary alloc]init];
obj=[parser objectWithString:responseString error:nil];
NSMutableArray *statuses = [[NSMutableArray alloc]init];
for (NSDictionary *status in obj)
{
[statuses addObject:status];
}
self.listdata=statuses;
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier: #"myCell"];
if (cell == nil) {
cell = [[ UITableViewCell alloc ] initWithFrame:CGRectZero];
}
NSUInteger row = [indexPath row];
cell.textLabel.text = [listdata objectAtIndex:row];
return cell;
Looks like your table view data source is not implementing the method tableView:numberOfRowsInSection:

iOS - Unable to access NSMutableArray in didSelectRowAtIndexPath?

I am unable to access my NSMutableArray in didSelectRowAtIndexPath although i am able to access it in cellForRowAtIndexPath.
Here is my code :-
- (void)viewDidLoad
{
NSString *path = [[NSBundle mainBundle] pathForResource:#"drinks" ofType:#"plist"];
self.drinkArray = [[NSMutableArray alloc] initWithContentsOfFile:path];
NSLog(#"%#", self.drinkArray);
[super viewDidLoad];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
NSLog(#"I am inside cellForRowAtIndexPath");
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
// Configure the cell.
NSDictionary *dict = [self.drinkArray objectAtIndex:indexPath.row];
cell.textLabel.text = [dict objectForKey:#"name"];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
[dict release];
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
DrinkDetails *detailViewController = [[DrinkDetails alloc] initWithNibName:#"DrinkDetails" bundle:nil];
// ...
// Pass the selected object to the new view controller.
//detailViewController.drink = [self.drinkArray objectAtIndex:indexPath.row];
NSLog(#"%#", self.drinkArray);
[self.navigationController pushViewController:detailViewController animated:YES];
[detailViewController release];
}
Sometime NSLog prints some stupid output and sometime it gives me an error "EXC_BAD_ACCESS".
Please take a look and check what is wrong with my code.
Any help would be appreciated.
Thanks.
You have one (really two) issues, but the main one is in your -cellForRowAtIndexPath: method:
[dict release];
Get rid of this line and it should work just fine.
The reason why this fixes your issue is because -objectAtIndex: simply returns a pointer to the requested object in memory, therefore you do not (and should not) send the -release message to that object because the NSArray obtained ownership of the object when it was inserted. Sending -release to this object reference effectively deallocates the object in memory and now this indice in the NSArray is pointing to garbage memory. BAD BAD BAD
The other issue is that you have a memory leak here:
self.drinkArray = [[NSMutableArray alloc] initWithContentsOfFile:path];
You are sending the -retain message to an object reference that you already have ownership of by way of sending -alloc. (This of course assumes that your #property has the retain setter modifier)
To fix this issue, simply send the -autorelease message to this instance:
self.drinkArray = [[[NSMutableArray alloc] initWithContentsOfFile:path] autorelease];
Don't release an object unless it was created with alloc or obtained with a method beginning with new or copy or explicitly retain'ed. (NARC)
In this case:
NSDictionary *dict = [self.drinkArray objectAtIndex:indexPath.row];
was not returned retained so you do not have ownership and should not release it.
By the same token:
self.drinkArray = [[NSMutableArray alloc] initWithContentsOfFile:path];
is alloc'ed so it needs to be released or obtained with a convince method that will return an autoreleased object:
self.drinkArray = [NSMutableArray arrayWithContentsOfFile:path];
You shouldn't [dict release]in cellForRowAtIndexPath. objectAtIndex does not retain it, so it may be sweeped out of your array when you release it.

iPhone UITableView populateing from connectionDidFinishLoading

I have been trying for hours to figure this out. I have some JSON from a NSURLConnection. This is working fine and I have parsed it into an array. But I can't seem to get the array out of the connectionDidFinishLoading method. I an am getting (null) in the UITableViewCell method. I am assuming this is a scope of retain issue, but I am so new to ObjC I am not sure what to do. Any help would be greatly appreciated.
Cheers.
-(void)connectionDidFinishLoading:(NSURLConnection *)connection {
[connection release];
NSString *responseString = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
[responseData release];
SBJSON *json = [[SBJSON alloc] init];
NSDictionary *results = [json objectWithString:responseString];
self.dataArray = [results objectForKey:#"data"];
NSMutableArray *tableArray = [[NSMutableArray alloc] initWithObjects:nil];
for (NSString *element in dataArray) {
//NSLog(#"%#", [element objectForKey:#"name"]);
NSString *tmpString = [[NSString alloc] initWithString:[element objectForKey:#"name"]];
[tableArray addObject:tmpString];
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
// Configure the cell.
//cell.textLabel.text = [self.tableView objectAtIndex:indexPath.row];
cell.textLabel.text = [self.tableArray objectAtIndex:indexPath.row];
NSLog(#"%#", tableArray);
return cell;
}
Solution:
After taking kubi and st3fan's advice with self.tableArray I found I had to reload the tableView with [self.tableView reloadData];
Get rid of the [connection release] in the 2nd line. The connection object comes in autoreleased, so this could cause crashes.
It looks like you've got a property named tableArray? If so, you're redeclaring the name in this method (you should have gotten a compiler warning).
On second thought, here's how the 2nd 1/2 of the method should look:
NSMutableArray *tmpArray = [[NSMutableArray alloc] initWithObjects:nil];
for (NSString *element in dataArray) {
[tmpArray addObject:[element objectForKey:#"name"]];
}
self.tableArray = tmpArray;
[tmpArray release];
In connectionDidFinishLoading: you use declare tableArray as a local variable. Therefore it will never be assigned to self.tableArray.
I had the same problem and I resolved it reloading the table.
I inserted this line at the end of connectionDidFinishLoading function
[self.tableView reloadData];
and it works.