iOS 5 JSON to tableView error - objective-c

I was trying to parse a JSON string from USGS. However I get error "-[__NSCFDictionary objectAtIndex:]: unrecognized selector sent to instance 0x68a34c0" Basically, I would like to parse the USGS JSON into the tableview. Can anyone help? Here are my codes.
ViewController.h
#interface EarthquakeViewController : UITableViewController
{
NSArray *json;
NSDictionary *earthquakeReport;
}
ViewController.m
- (void)viewDidLoad
{
//content = [NSMutableArray arrayWithObjects:#"one", #"two", nil];
[super viewDidLoad];
[self fetchReports];
}
- (void)fetchReports
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSData* data = [NSData dataWithContentsOfURL:
[NSURL URLWithString: #"http://earthquake.usgs.gov/earthquakes/feed/geojson/all/hour"]];
NSError* error;
json = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:&error];
NSLog(#"%#", json);
dispatch_async(dispatch_get_main_queue(), ^{
[self.tableView reloadData];
});
});
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
return json.count;
}
- (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];
}
earthquakeReport = [json objectAtIndex:indexPath.row];
NSString *country = [[[earthquakeReport objectForKey:#"features"] objectForKey:#"properties"] objectForKey:#"place"];
NSString *mag = [[[earthquakeReport objectForKey:#"features"] objectForKey:#"properties"] objectForKey:#"mag"];
cell.textLabel.text = country;
cell.detailTextLabel.text = mag;
return cell;
}
The error is showing at the line earthquakeReport = [json objectAtIndex:indexPath.row];

If you look at your JSON data in a web browser (http://earthquake.usgs.gov/earthquakes/feed/geojson/all/hour) you should notice that the array you are trying to access is not at the root level. The root level is a dictionary, and the array you're looking for is in the "features" key.
To access it properly, first change your json ivar declaration into an NSDictionary:
NSDictionary *json;
Then, in your tableView:cellForRowAtIndexPath: method, access the report thusly:
earthquakeReport = [[json objectForKey:#"features"] objectAtIndex:indexPath.row];

Related

Populating Custom TableViewCell from Twitter using JSON Serialization

I am new to Objective-C. I spent countless hours being stuck on getting a blank tableview, I am desperate at this point.
I am loading twitter data through a JSON call, using their API. I store everything in a NSDictionary, run a for loop to select only "text" values. I store the filtered dictionary in an object which I later use in the TableView initialization.
I created a subclass of UItableViewCell for my custom cell.
My dataSources and delegates seem to be well connected as well (that is what I think at least)
I am having a hard time finding my problem. If someone could help me out please.
#import "ViewController.h"
#import "myCell.h"
#import <TwitterKit/TwitterKit.h>
#interface ViewController ()
#end
#implementation ViewController
#synthesize myTableView;
NSMutableArray *tweetObject;
NSDictionary *dictionary;
NSString *name;
NSString *text;
- (void)viewDidLoad {
tweetObject = [[NSMutableArray alloc] init];
[super viewDidLoad];
self.myTableView.dataSource = self;
self.myTableView.delegate = self;
text = #"text";
[[Twitter sharedInstance] logInGuestWithCompletion:^(TWTRGuestSession *guestSession, NSError *error) {
if (guestSession) {
// make API calls that do not require user auth
NSString *statusesShowEndpoint = #"https://api.twitter.com/1.1/statuses/user_timeline.json?screen_name=goofernator";
NSError *clientError;
NSURLRequest *request = [[[Twitter sharedInstance] APIClient]
URLRequestWithMethod:#"GET"
URL:statusesShowEndpoint
parameters:0
error:&clientError];
if (request) {
[[[Twitter sharedInstance] APIClient]
sendTwitterRequest:request
completion:^(NSURLResponse *response,
NSData *data,
NSError *connectionError) {
if (data) {
// handle the response data e.g.
NSError *jsonError;
NSDictionary *json = [NSJSONSerialization
JSONObjectWithData:data
options:0
error:&jsonError];
for (NSDictionary *dataDict in json) {
NSString *text = [dataDict valueForKeyPath: #"text"];
dictionary = [NSDictionary dictionaryWithObjectsAndKeys:text,#"bodytext",nil];
[tweetObject addObject:dictionary];
}
}
else {
NSLog(#"Error: %#", connectionError);
}
}];
}
else {
NSLog(#"Error: %#", clientError);
}
} else {
NSLog(#"error: %#", [error localizedDescription]);
}
}];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return tweetObject.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
myCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell=[[myCell alloc]initWithStyle:
UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
NSDictionary *tmpDict = [self.tweetObject objectAtIndex:indexPath.row];
cell.txtLblOutput.text = [tmpDict objectForKey:text];
[tableView reloadData];
return cell;
}
#end
Here you can see the way my storyboard is put together and the references I am using
http://postimg.org/image/79w7pqmu3/
http://postimg.org/image/ixq9kabyz/
You should call [tableView reloadData]; in your request completion handler, after you've filled your array. Check if you receive any data. Does the array get filled with dictionary objects?
But mate, seriously, you need to read some good books about coding, your code really lacks understanding of what you're doing. Please, remove [tableView reloadData]; from the - ...cellForRowAtIndexPath:... method.

Error when swipe to delete a table cell

I'm trying to delete a row when user swipes. I got this error driving me crazy. I've spent last three hours trying to figure out why. But, I have got no clue so far.
Here is my code to accomplish that.
in .h
#import <UIKit/UIKit.h>
#import "CustomCell.h"
#interface FollowersTableViewController : UITableViewController
#property (nonatomic,strong)NSMutableArray *arrayWithUser ;
#end
and in .m i have this code .
#import "FollowersTableViewController.h"
#implementation FollowersTableViewController
#synthesize arrayWithUser ;
- (id)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style];
if (self) {
// Custom initialization
}
return self;
}
-(void)viewDidLoad
{
[super viewDidLoad];
NSDictionary *dicUrlList= [NSDictionary dictionaryWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"Urls" ofType:#"plist"]];
NSString *baseURl = [dicUrlList objectForKey:#"urlWithUser"];
baseURl = [baseURl stringByAppendingFormat:#"getfollowers"];
NSURL *urlToGetFollowers = [NSURL URLWithString:baseURl];
NSURLRequest *request = [NSURLRequest requestWithURL:urlToGetFollowers];
NSError *error = nil ;
NSURLResponse *response = nil ;
NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
arrayWithUser = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableLeaves error:nil];
}
- (void)viewDidUnload
{
[super viewDidUnload];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [arrayWithUser count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *MyIdentifier = #"Cell";
CustomCell *customCell = (CustomCell *)[tableView dequeueReusableCellWithIdentifier:MyIdentifier];
if (customCell == nil)
{
customCell = [[CustomCell alloc] initWithFrame:CGRectMake(0, 0, 320, 50)] ;
}
NSDictionary *dicWithUser = [arrayWithUser objectAtIndex:indexPath.row];
NSString *photoUrl = [dicWithUser objectForKey:#"profilePhotoUrl"];
if(![photoUrl isEqualToString:#""])
[customCell.thumbnail setImageWithURL:[dicWithUser objectForKey:#"profilePhotoUrl"] placeholderImage:[UIImage imageNamed:#"placeholder.png"] ];
else
{
[customCell.thumbnail setImage:[UIImage imageNamed:#"placeholder.png"]];
}
customCell.titleLabel.text = [dicWithUser objectForKey:#"username"];
UIButton *buttonFollow = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[buttonFollow setTitle:#"Follow" forState:UIControlStateNormal];
CGRect frame = buttonFollow.frame ;
frame = CGRectMake(200, 10, 60, 30);
buttonFollow.frame = frame ;
buttonFollow.tag = indexPath.row ;
[buttonFollow addTarget:self action:#selector(followButtonPressed:) forControlEvents:UIControlEventTouchUpInside];
customCell.accessoryView = buttonFollow ;
return customCell;
}
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 60 ;
}
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
{
// Return NO if you do not want the specified item to be editable.
return YES;
}
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle == UITableViewCellEditingStyleDelete) {
// Delete the row from the data source
[arrayWithUser removeObjectAtIndex:indexPath.row];
}
}
So far,I'm able to see delete button, but when i pressed it gives me this error
[__NSCFArray removeObjectAtIndex:]: mutating method sent to immutable object.
Since I've already used NSMutableArray, I've no idea, why I'm getting this error?
I've already try to clean project. It didn't make any difference.
Your Json call returning an NSArray. You can go to create a mutableCopy - so you're will be able to use the "removeAtIndex.." method.
NSArray *rData = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableLeaves error:nil];
arrayWithUser = [rData mutableCopy];
The assignment to arrayWithUser from the JSON call is returning an NSArray not an NSMutableArray in viewDidLoad. Fix that.
Actually, your array(arrayWithUser) is strongly pointing to the array return by the JSONObjectWithData, as you don't have the ownership of the returned array, you can't remove its object.
You better do one thing ,take ownership of that array.
arrayWithUser = [[NSMutableArray alloc]arrayByAddingObjectsFromArray:[NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableLeaves error:nil]];
Just replace below line
arrayWithUser = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableLeaves error:nil];
with
arrayWithUser = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:nil];
From Apple's documentation:
NSJSONReadingMutableContainers: Specifies that arrays and dictionaries are created as mutable objects.
NSJSONReadingMutableLeaves: Specifies that leaf strings in the JSON object graph are created as instances of NSMutableString.
You used in viwDidLoad
arrayWithUser = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableLeaves error:nil];
Lets try like this
NSMutableArray *userMutarr = [NSMutableArray alloc]initWithCapacity:3];
self.arrayWithUser = userMutarr;
[userMutarr release];
Then
self.arrayWithUser = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableLeaves error:nil];

When scrolling in my UITableView cells are disappearing

This is my sample twitter program it works but there are several errors
if I scroll up or down top and bottom cellls disappears some times I get an error saying :
BAD ACCESS CODE this happens on this row
NSDictionary *tweet = [tweets objectAtIndex:indexPath.row];
Please Advice
#import "TableViewViewController.h"
#interface TableViewViewController ()
#end
#implementation TableViewViewController
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
NSLog(#"tweets array count : %d", tweets.count);
return tweets.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"TweetCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
NSLog(#"ROW : %d", indexPath.row);
NSDictionary *tweet = [tweets objectAtIndex:indexPath.row];
NSString *text = [tweet objectForKey:#"text"];
NSString *name = [[tweet objectForKey:#"user"] objectForKey:#"name"];
cell.textLabel.text = text;
cell.detailTextLabel.text = [NSString stringWithFormat:#"by %#", name];
return cell;
}
- (void)viewDidLoad
{
[super viewDidLoad];
[self fetchTweets];
}
- (void)fetchTweets
{
NSString *twitterURL = [NSString stringWithFormat:#"https://api.twitter.com/1/statuses/public_timeline.json"];
NSURL *fullURL = [NSURL URLWithString:twitterURL];
NSError *error = nil;
NSData *dataURL = [NSData dataWithContentsOfURL:fullURL options:0 error:&error];
tweets = [NSJSONSerialization JSONObjectWithData:dataURL
options:kNilOptions
error:&error];
}
.....
#end
I see problems in this function:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
NSLog(#"tweets array count : %d", tweets.count);
//return tweets.count;
return 11; //default returns 20 but only shows 10 indexpath.Row so 11
}
You probably shouldn't be just returning 11 -- what if tweets has a different length? The table view will try to fetch a cell at an index which doesn't exist in your array. Try returning tweets.count instead.
To elaborate a bit further: The purpose of the tableView:numberOfRowsInSection: method isn't to tell iOS how many rows there are on the screen, it's how many rows there are in the entire tableView. This includes cells which aren't shown on screen.
Found out the answer My app doesn't support ARC because I didn't check if we use a retain at the NSJSONSerialization the error is fixed.
tweets = [[NSJSONSerialization JSONObjectWithData:dataURL
options:kNilOptions
error:&error] retain];
Why is numberOfRowsInSection hardcoded to 11?
Also you should have [self.tableView reloadData] after the tweets array is set up in fetchTweets
Why don't you just uncomment return tweets.count; in your numberOfRowsInSection method?

Custom cell not showing up in tableView

I have a custom cell with identifyer 'tweetCell' I have imported this into my tableviewcontroller.h file. I have linked the class to the prototype cell in storyboard. All the UILabels are wired up to the cell but I cannot get the tableview to display the custom cell. It did work? Then overnight stopped?!! I understand that class files need to start with an upper case letter, I have changed this but to no avail. Can anyone spot my error here? Thanks in advance......
- (void)fetchTweets
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSData* data = [NSData dataWithContentsOfURL:
[NSURL URLWithString: #"http://search.twitter.com/search.json?q=%23mysearchhashtag"]];
NSError* error;
tweets = [NSJSONSerialization JSONObjectWithData:data
options:kNilOptions
error:&error];
dispatch_async(dispatch_get_main_queue(), ^{
[self.tableView reloadData];
});
});
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return tweets.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"tweetCell";
customCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[customCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
NSDictionary *tweet = [tweets objectAtIndex:indexPath.row];
NSString *tweetText = [tweet valueForKey:#"text"];
NSArray *tweetComponents = [tweetText componentsSeparatedByString:#":"];
cell.firstDetail.text = [tweetComponents objectAtIndex:0];
cell.secondDetail.text = [tweetComponents objectAtIndex:1];
cell.thirdDetail.text = [tweetComponents objectAtIndex:2];
return cell;
}
Did you write this method - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView? Try like this.
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}

Cocoa Touch UITableView Data

I am working for the first time in objective c and have come across an issue that I have not seen an answer for.
I am loading a set of data from a JSON data set and using it to populate a UITableView within a UITableViewController.
First when the view is loaded (viewDidLoad) I populate the array with the JSON data from a URL.
Next the data loads as it should. numberOfRowsInSection shows that there are 30 results in the array which is correct.
However The Iphone loads the entire set three times into the tableview.
Here is some code from the controller for that view:(Twitter is being used as an example and is not the actual data set I use)
- (void)viewDidLoad {
[super viewDidLoad];
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem;
//results = [[NSMutableArray alloc] init];
self.navigationItem.title=#"public_timeline";
self.tableView.allowsSelection = NO;
NSString *url = [[NSString alloc] init];
url = [[NSString alloc] initWithFormat:#"http://twitter.com/statuses/%s.json",#"public_timeline"];
if ([results count] == 0) {
[self parseJSON:url];
}
}
Here is the parseJSON (actual parse is done with the Cocoa JSON framework
-(void) parseJSON:(NSString *)URL{
NSURL *JSONURL = [NSURL URLWithString:URL];
NSData *responseData = [[NSData alloc] initWithContentsOfURL:JSONURL];
NSString *responseString = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
results = [[NSMutableArray alloc] initWithArray:[responseString JSONValue]];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [results count];
}
then the cell's output
- (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];
// Set up the cell...
}
NSDictionary *cdict = [results objectAtIndex:indexPath.row];
cell.textLabel.text = [cdict valueForKey:#"text"];
return cell;
}
I am not sure if what I am doing is the best way to do this, so if someone could help me out that would be great.
Check the numberOfSectionsInTableView Method and change the return Value to 1
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}