redundant calculations slowing down UITableView - ios

I am creating a scrollable UITableView in which each cell requires to make a call to the Google Directions Matrix API.For some reason,each time I scroll it makes the call and does the calculations separately which significantly takes a toll on the responsiveness of the scrolling.Here's the code:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
if(tableView.tag == 1) {
return [self specialTableViewCell: tableView];
}
static NSString *CellIdentifier = #"OfferCell";
OTNOfferCell *cell = (OTNOfferCell*)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
// create cell using style Subtitle
cell = [[OTNOfferCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:CellIdentifier];
}
PFObject *venue = [self.venues objectAtIndex:indexPath.section];
cell.backgroundView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"MainPage Item.png"]];
cell.selectedBackgroundView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"MainPageItemSelected.png"]];
cell.clubNameLabel.text = [venue valueForKey: #"name"] ;
PFFile *photo = [venue objectForKey:#"logo"];
NSData *data = [photo getData];
UIImage *image = [UIImage imageWithData: data];
cell.clubLogoImageView.image = image;
int count = [[venue valueForKey:#"events"] count];
if(count == 1)
{
cell.numberOfEventsLabel.text = #"1 Event";
}
else
{
cell.numberOfEventsLabel.text = [NSString stringWithFormat:#"%d Events", count];
}
PFGeoPoint *destinationLocation = [venue objectForKey:#"geopoint"];
locationManager = [[CLLocationManager alloc] init];
locationManager.distanceFilter = kCLDistanceFilterNone;
locationManager.desiredAccuracy = kCLLocationAccuracyHundredMeters;
[locationManager startUpdatingLocation];
PFGeoPoint *currentLocation = [PFGeoPoint geoPointWithLatitude:locationManager.location.coordinate.latitude longitude:locationManager.location.coordinate.longitude];
NSString *current = [venue objectForKey:#"address"];
NSLog(#"%#",current);
NSString *urlString = [NSString stringWithFormat:#"http://maps.googleapis.com/maps/api/distancematrix/json?origins=%#&destinations=San+Francisco&mode=driving&language=en&sensor=true&units=imperial",current];
NSURL *url = [NSURL
URLWithString:[urlString
stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
NSData *googledata = [NSData dataWithContentsOfURL:url];
NSError *error;
NSMutableDictionary *json = [NSJSONSerialization JSONObjectWithData:googledata options:kNilOptions error:&error];
NSString *result = [[[[[[json objectForKey:#"rows"] objectAtIndex: 0] objectForKey:#"elements"] objectAtIndex:0]objectForKey:#"distance"]objectForKey:#"text"];
double tempd = [currentLocation distanceInMilesTo:destinationLocation];
NSString *distance = [NSString stringWithFormat:#"%f",tempd];
NSString *distanceTrunc = [distance substringToIndex: MIN(3, [distance length])];
cell.distanceLabel.text = [NSString stringWithFormat:#"%# mi", distanceTrunc];
return cell;
}
Is there any way to fix this,wherein the calculation is done only once.

You should not be making these calculations in the cell. UITableViewCells are part of the view layer.
You should make the requests in your controller and store the results in something like an NSArray. Then cellForRowAtIndexPath should just pull data from that array.

Related

How to get the parsed json data inside the tableview cell in objective c?

Here I parsed my json data and kept in json and I have to insert into the tableviewcell which is designed custom...here I posted the json parsed code and tableview cell...can somebody please help me with this...
NSString *urlstring =#"https://bsedemo.com/instasocial/api/post/feeds?user_id=402&&access_token=$2y$10$TWX2Ym1gM4NBjGCNzTydzuQTZWPcK2tMVw1eWdWvYDFmO4CsKc1kC&&page_no=1&&feed_type=3&&feed_id=&&member_id=402";
NSURL *url = [NSURL URLWithString:urlstring];
[[NSURLSession.sharedSession dataTaskWithURL:url completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error)
{
NSError *err;
NSDictionary * jsondata = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:nil];
NSMutableArray *json =[[NSMutableArray alloc] initWithObjects:[jsondata objectForKey:#"result"],nil];
if(err)
{
NSLog(#"Failed to load json:%#",err);
return;
}
for (int i=0;i<json.count;i++)
{
NSLog(#"json:%#",json[i]);
}
for (NSDictionary *dict in json) {
self->user_image = [dict valueForKey:#"user_image"];
self->username = [dict valueForKey:#"user_name"];
self->created_at =[dict valueForKey:#"created_at"];
self->post_text = [dict valueForKey:#"post_text"];
self->post_comment_count = [dict valueForKey:#"post_comment_count"];
self->post_like_count = [dict valueForKey:#"post_like_count"];
//self->media_img = dict[#"media"][0][#"media_image"];
}
}]resume];
[self->tableview reloadData];
Here I posted the tableview and tableviewcell code which I tried...if I done wrong please correct the code...Thanks in advance!!
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return 10;//name.count;
}
- (nonnull UITableViewCell *)tableView:(nonnull UITableView *)tableView cellForRowAtIndexPath:(nonnull NSIndexPath *)indexPath
{
static NSString *simpleTableIdentifier = #"Cell";
TableViewCellVC *cell = [[TableViewCellVC alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:simpleTableIdentifier];
/*TableViewCellVC *cell = (TableViewCellVC *)*/
cell=[tableview dequeueReusableCellWithIdentifier:simpleTableIdentifier forIndexPath:indexPath];
if (cell == nil)
{
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"TableViewCellVC" owner:self options:nil];
cell = [nib objectAtIndex:0];
}
cell.profileImageView.image = [UIImage imageNamed:#"girl1.jpg"]; //[UIImage imageNamed:[user_image objectAtIndex:indexPath.row]];
cell.profileImageView.layer.cornerRadius = 23.5;
//cell.profileImageView.backgroundColor = [UIColor blackColor];
//[cell.usernameLabel setFont:[UIFont fontWithName:#"Raleway-Thin" size:15]];
cell.usernameLabel.text =[name objectAtIndex:indexPath.row];//[username objectAtIndex:indexPath.row];
//[cell.lastseenLabel setFont:[UIFont fontWithName:#"Raleway-Thin" size:12]];
cell.lastseenLabel.text = #"Today at 23:52";//[created_at objectAtIndex:indexPath.row];
//[cell.discriptionLabel setFont:[UIFont fontWithName:#"Raleway-Thin" size:10]];
cell.discriptionLabel.text =#"All our dreams can come true, if we have the courage to pursue them. – Walt Disney. All our dreams can come true, if we have the courage to pursue them. – Walt Disney.";//[post_text objectAtIndex:indexPath.row];
cell.postImageView.image = [UIImage imageNamed:#"postpic.jpg"];
//media_img[indexPath.row];
// [cell.likeLabel setFont:[UIFont fontWithName:[NSString fontAwesomeIconStringForEnum:FAHeart] size:16]];
cell.likeLabel.text =[NSString fontAwesomeIconStringForEnum:FAHeart];
//[post_like_count objectAtIndex:indexPath.row];
cell.commentLabel.text =[NSString fontAwesomeIconStringForEnum:FAComment]; //[post_comment_count objectAtIndex:indexPath.row];
cell.shareLabel.text=[NSString fontAwesomeIconStringForEnum:FAShare];
cell.moreLabel.text=[NSString fontAwesomeIconStringForEnum:FADotCircleO];
return cell;
}
-(void)tableView
{
tableview = [[UITableView alloc] initWithFrame:CGRectMake(0,topbar.frame.origin.y+topbar.frame.size.height,[UIScreen mainScreen].bounds.size.width,[UIScreen mainScreen].bounds.size.height-150) style:UITableViewStylePlain];
tableview.delegate = self;
tableview.dataSource = self;
//tableview.rowHeight=UITableViewAutomaticDimension;
//tableview.estimatedRowHeight=400;
tableview.separatorColor = [UIColor blackColor];
[tableview registerNib:[UINib nibWithNibName:#"TableViewCellVC" bundle:nil]
forCellReuseIdentifier:#"Cell"];
[mainview addSubview:tableview];
}

Populate UITableView via a string?

I have a simple iOS app which parses multiple JSON feeds and stores the data in multiple strings. I know exactly which strings to use for what and how long the count is because the JSON feeds are feeds that I control from some of my websites.
However, even though I have specified this in the "tableView cellForRowAtIndexPath" method, the UITableView still won't populate..
Is this because I am using strings to populate the UITableView? And if so, do you HAVE to use arrays to populate a UITableView.
Thanks for you're time :)
UPDATE: Here is m code:
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return 5;
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSLog(#"Printing table view.");
static NSString *CellIdentifier = #"Cell";
AccountCell *cell = (AccountCell *)[account_table dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"AccountCell" owner:self options:nil];
cell = [nib objectAtIndex: 0];
// Draws the cell background.
cell.backgroundView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"uiFeedletsnglass5.png"]];
// Draws the pressed cell background.
cell.selectedBackgroundView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"cell-on.png"]];
// Round of edges of content view in Carousel.
cell.layer.cornerRadius = 5;
cell.layer.masksToBounds = YES;
cell.layer.borderWidth = 1.0f;
cell.layer.borderColor = [[UIColor grayColor] CGColor];
cell.profilepic.layer.cornerRadius = 5;
cell.profilepic.layer.masksToBounds = YES;
cell.profilepic.layer.borderWidth = 1.0f;
cell.profilepic.layer.borderColor = [[UIColor grayColor] CGColor];
}
if ((facebook_printed == 0) && (logged_facebook == 1)) {
NSString *full_name = [NSString stringWithFormat:#"%# %#", facebook_first_name, facebook_last_name];
cell.username.text = [NSString stringWithFormat:#"%#", full_name];
cell.account_type_name.text = [NSString stringWithFormat:#"Facebook"];
NSData *facebook_imageData = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString: facebook_proffile_pic]];
UIImage *facebook_image = [[UIImage alloc] initWithData:facebook_imageData];
cell.profilepic.image = facebook_image;
facebook_printed = 1;
}
else if ((youtube_printed == 0) && (logged_youtube == 1)) {
cell.username.text = [NSString stringWithFormat:#"%#", youtube_profilename];
cell.account_type_name.text = [NSString stringWithFormat:#"YouTube"];
NSData *youtube_imageData = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString: youtube_profilepic]];
UIImage *youtube_image = [[UIImage alloc] initWithData:youtube_imageData];
cell.profilepic.image = youtube_image;
youtube_printed = 1;
}
else if ((instagram_printed == 0) && (logged_instagram == 1)) {
cell.username.text = [NSString stringWithFormat:#"%#", instagram_name_tag];
cell.account_type_name.text = [NSString stringWithFormat:#"Instagram"];
NSData *instagram_imageData = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString: instagram_profilepicture]];
UIImage *instagram_image = [[UIImage alloc] initWithData:instagram_imageData];
cell.profilepic.image = instagram_image;
instagram_printed = 1;
}
else if ((googleplus_printed == 0) && (logged_googleplus == 1)) {
cell.username.text = [NSString stringWithFormat:#"%#", googleplus_profilename];
cell.account_type_name.text = [NSString stringWithFormat:#"Google Plus"];
NSData *googleplus_imageData = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString: googleplus_profilepic]];
UIImage *googleplus_image = [[UIImage alloc] initWithData:googleplus_imageData];
cell.profilepic.image = googleplus_image;
googleplus_printed = 1;
}
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
return cell;
}
Maybe try reloading the table view's data once you obtain the value of the string?
[tableView reloadData];
Right after much frustration and pretty much a lot of trial and error I finally figured out that it is because of if (cell == nil) that my Custom Cell was not loading (showing in the UITableView).
I was not aware of this at all, but from what I have read online it seems that when using UiTableViews in Storyboard UI's with Custom Cells, you are NOT meant to use the control statement if (cell == nil)
Thanks to everyone who commented on this post though. I appreciate you're help.

Why is my UITabeViewContent reorders everytime I refresh it?

i have a UITebleView with costume UITableViewCells. Every time I refresh the them the content is reordering itself. anyone know why?
I am fatching the data from a JSON, I dont do some sort of sorting, I just display the data acording to the TableViewCell indexpath.row
And this is the code I set the UITableViewCell content:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *costumeCell = #"Cell";
StoreCell *cell = [tableView dequeueReusableCellWithIdentifier:costumeCell];
if (!cell) {
cell = [[StoreCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:costumeCell];
}
NSDictionary *dict;
dict = [application objectAtIndex:indexPath.row];
[downloadQueue addOperationWithBlock:^{
name = [dict objectForKey:#"name"];
detileName = [dict objectForKey:#"detailName"];
itmsLink = [dict objectForKey:#"itms-serviceLink"];
icon = [dict objectForKey:#"icon"];
developer = [dict objectForKey:#"developer"];
version = [dict objectForKey:#"version"];
category = [dict objectForKey:#"category"];
rating = [dict objectForKey:#"rating"];
ratingNumbers = [dict objectForKey:#"ratingNumber"];
description = [dict objectForKey:#"description"];
developerEmails = [dict objectForKey:#"developerEmail"];
cell.AppName.text = name;
cell.category.text = category;
cell.rater.text = [NSString stringWithFormat:#"(%#)", ratingNumbers];
if ([rating intValue] == 1) {
cell.rating.image = [UIImage imageNamed:#"1.png"];
}
if ([rating intValue] == 2) {
cell.rating.image = [UIImage imageNamed:#"2.png"];
}
if ([rating intValue] == 3) {
cell.rating.image = [UIImage imageNamed:#"3.png"];
}
if ([rating intValue] == 4) {
cell.rating.image = [UIImage imageNamed:#"4.png"];
}
cell.itms = itmsLink;
[NSURLConnection sendAsynchronousRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:icon]] queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse* response, NSData* data, NSError* error){
if(error)
{
// Error Downloading image data
cell.AppIcon.image = [UIImage imageNamed:#"placeholder.png"];
}
else
{
[cell.AppIcon setImage:[UIImage imageWithData:data]];
}
}];
cell.AppIcon.image = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:icon]]];
cell.number.text = [NSString stringWithFormat:#"%li", (long)indexPath.row + 1];
}];
cell.AppIcon.layer.masksToBounds = YES;
cell.AppIcon.layer.cornerRadius = 16.0;
cell.installButton.layer.masksToBounds = YES;
cell.installButton.layer.cornerRadius = 5.0f;
cell.installButton.layer.borderColor = [UIColor darkGrayColor].CGColor;
cell.installButton.layer.borderWidth = 1.0f;
return cell;
}
Since you're fetching from JSON, the server you got it from may not sort your data for you. Course its impossible for us to know what its doing without you divulging into the the server's APIs.
What you can do, without giving further info is just do a sort after you receive new data from the server:
NSSortDescriptor *descriptor = [[NSSortDescriptor alloc] initWithKey:#"name" ascending:YES];
NSArray *sortedApplicationArray = [application sortedArrayUsingDescriptors:#[descriptor]];
application = sortedApplicationArray;
Just don't put that code inside your cellForRowAtIndexPath, cos it'll do a sort each time a row is created!
I think I know what the problem is.
You have an asynchronous operation in your cellForRowAtIndexPath. You should set the cell UI elements directly with the dict object and outside the operation queue.
The only thing that looks like it needs to be done asynchronously is the image download. I recommend you use SDWebImage from github for that, it handles the download in its own queue and caches the image in memory and on disk.

UITableView stops scrolling

My TableView, which uses custom cells, sometimes 'stops' scrolling when I return from a DetailView.
I can push the view up a couple inches or so, and the lower cells do come into view, but the scrollview should be 4 or 5 windows in height.
The detail view is called through a segue. In addition to the back button, I also trigger a return with a swipe gesture. Either way it happens. - not all the time though.
Hope this is enough info. Does anyone have any ideas what I am screwing up?
Thanks!
Here's some code:
- (NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath{
int row = [indexPath row];
int sect = [indexPath section];
NSDictionary *shift = [globals.shifts objectAtIndex:sect];
NSLog(#"\nShift = %#", shift);
NSLog(#"\nEmployees = %#", [shift objectForKey:#"Employees"]);
NSArray *employees = [shift objectForKey:#"Employees"];
NSDictionary *employee = [employees objectAtIndex:row];
globals.badge = [employee objectForKey:#"Badge"];
globals.name = [employee objectForKey:#"Name"];
return indexPath;
}
- (void)receivedData:(NSData *)responseData {
NSError* error;
globals.report = [NSJSONSerialization
JSONObjectWithData:responseData
options:kNilOptions
error:&error];
globals.shifts = [globals.report objectForKey:#"shifts"];
[globals.ai removeFromSuperview];
[table reloadData];
self.title = [ [NSString alloc] initWithFormat:#"%# - %#",
[globals.clientNames objectAtIndex:globals.curClient],
[self.dateFormatter stringFromDate: globals.weekEnd]];
self.title = [ [NSString alloc] initWithFormat:#"%#",
[self.dateFormatter stringFromDate: globals.weekEnd]];
}
-(void) viewWillAppear:(BOOL)animated {
if( globals == nil || globals.report == nil )
{
appDelegate = (MSIAppDelegate *)[[UIApplication sharedApplication] delegate];
globals = [appDelegate globals];
[[self view] addSubview:globals.ai];
NSString *urlString = [[NSString alloc] initWithFormat:
#"http://www.msiwebtrax.com/Client/%#/WeeklyReport?id=%#&start=%#&end=%#", [globals.clientIDs objectAtIndex:globals.curClient], globals.userName, [globals.formatter stringFromDate: globals.weekEnd], [globals.formatter stringFromDate: [globals.weekEnd dateByAddingTimeInterval:(-7) * 24 * 60 * 60] ]];
NSURL *url = [NSURL URLWithString: urlString];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0 ), ^{
NSData* data = [NSData dataWithContentsOfURL:
url];
[self performSelectorOnMainThread:#selector(receivedData:)
withObject:data waitUntilDone:YES];
});
}
}
- (void)viewDidLoad
{
[super viewDidLoad];
UIScrollView *tempScrollView=(UIScrollView *)self.view;
NSLog(#"scroll view dimensions: %f, %f", tempScrollView.contentSize.height, tempScrollView.contentSize.width);
self.dateFormatter = [[NSDateFormatter alloc] init];
[self.dateFormatter setDateStyle:NSDateFormatterMediumStyle];
[self.dateFormatter setTimeStyle:NSDateFormatterNoStyle];
// Uncomment the following line to preserve selection between presentations.
self.clearsSelectionOnViewWillAppear = NO;
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem;
}
Here's the number of sections/rows
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
int sections = [globals.shifts count];
return sections;
}
- (NSInteger) tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
NSDictionary* shift = [globals.shifts objectAtIndex:section];
int rowCount = [[shift objectForKey:#"Employees"] count];
return rowCount;
}
--
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
static NSString *CellIdentifier = #"EmployeeHoursCell";
EmployeeHoursCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if( cell == nil ) {
cell = [[EmployeeHoursCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
NSUInteger section = [indexPath section];
NSUInteger row = [indexPath row];
globals.employees = [[globals.shifts objectAtIndex:section] objectForKey:#"Employees"];
cell.Row.text = [[NSString alloc] initWithFormat:#"%3d", (row+1)];
cell.Id.text = [[globals.employees objectAtIndex:row] objectForKey:#"Badge"];
cell.Name.text = [[globals.employees objectAtIndex:row] objectForKey:#"Name"];
NSArray *days = [[globals.employees objectAtIndex:0] objectForKey:#"Days"];
cell.Hrs1.text = [[NSString alloc] initWithFormat:#"%#",[[days objectAtIndex:0] objectForKey:#"Rounded"] ];
cell.Hrs1.text = [[NSString alloc] initWithFormat:#"%.2f", [cell.Hrs1.text doubleValue]];
cell.Hrs2.text = [[NSString alloc] initWithFormat:#"%#",[[days objectAtIndex:1] objectForKey:#"Rounded"] ];
cell.Hrs2.text = [[NSString alloc] initWithFormat:#"%.2f", [cell.Hrs2.text doubleValue]];
cell.Hrs3.text = [[NSString alloc] initWithFormat:#"%#",[[days objectAtIndex:2] objectForKey:#"Rounded"] ];
cell.Hrs3.text = [[NSString alloc] initWithFormat:#"%.2f", [cell.Hrs3.text doubleValue]];
cell.Hrs4.text = [[NSString alloc] initWithFormat:#"%#",[[days objectAtIndex:3] objectForKey:#"Rounded"] ];
cell.Hrs4.text = [[NSString alloc] initWithFormat:#"%.2f", [cell.Hrs4.text doubleValue]];
cell.Hrs5.text = [[NSString alloc] initWithFormat:#"%#",[[days objectAtIndex:4] objectForKey:#"Rounded"] ];
cell.Hrs5.text = [[NSString alloc] initWithFormat:#"%.2f", [cell.Hrs5.text doubleValue]];
cell.Hrs6.text = [[NSString alloc] initWithFormat:#"%#",[[days objectAtIndex:5] objectForKey:#"Rounded"] ];
cell.Hrs6.text = [[NSString alloc] initWithFormat:#"%.2f", [cell.Hrs6.text doubleValue]];
cell.Hrs7.text = [[NSString alloc] initWithFormat:#"%#",[[days objectAtIndex:6] objectForKey:#"Rounded"] ];
cell.Hrs7.text = [[NSString alloc] initWithFormat:#"%.2f", [cell.Hrs7.text doubleValue]];
return cell;
}
Okay all - in the viewWillAppear method, I tried reloading the table, which didn't have any impact. But I then reset the contentSize to 0,0 before and it seems to work fine now, not entirely sure why - but I noticed the first time I come back from a detailview, the height is set to around 2000, the second time I return it is zero - at which point it works. So I set it to zero and reload.
Thanks Darren and all for the responses!

JSON parsing returns null to iOS (json string looks correct)

I am trying to get a JSON to iOS app, but keep getting NULL values..
- (id)initWithDictionary:(NSDictionary *)dictionary {
self.name = [dictionary valueForKey:#"name"];
self.amount = [NSString stringWithFormat:#"%#",
[dictionary valueForKey:#"amount"]];
self.goalId = [dictionary valueForKey:#"id"];
self.createdAt = [dictionary valueForKey:#"created_at"];
self.updatedAt = [dictionary valueForKey:#"updated_at"];
return self;
}
+ (NSArray *)findAllRemote {
NSURL *url = [NSURL URLWithString:#"http://localhost:3000/goals.json"];
NSError *error = nil;
NSString *jsonString =
[NSString stringWithContentsOfURL:url
encoding:NSUTF8StringEncoding
error:&error];
NSLog(#"my string = %#", jsonString);
NSMutableArray *goals = [NSMutableArray array];
if (jsonString) {
SBJSON *json = [[SBJSON alloc] init];
NSArray *results = [json objectWithString:jsonString error:&error];
[json release];
for (NSDictionary *dictionary in results) {
Goal *goal = [[Goal alloc] initWithDictionary:dictionary];
[goals addObject:goal];
[goal release];
}
}
return goals;
}
JSON string looks correct:
my string = [{"goal":{"amount":"100.0","created_at":"2011-08-20T00:55:34Z","id":1,"name":"User","updated_at":"2011-08-20T00:55:34Z"}},{"goal":{"amount":"200.0","created_at":"2011-08-20T00:56:48Z","id":2,"name":"User2","updated_at":"2011-08-20T00:56:48Z"}},{"goal":{"amount":"19999.0","created_at":"2011-08-20T19:15:10Z","id":3,"name":"This is MY GOAL","updated_at":"2011-08-20T19:15:10Z"}},{"goal":{"amount":"0.0","created_at":"2011-08-20T20:46:44Z","id":4,"name":"goal","updated_at":"2011-08-20T20:46:44Z"}}]
I am missing something basic I guess..
UPDATE:
Here is a line that returns NULL (from another class):
- (IBAction)refresh {
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
self.goals = [Goal findAllRemote];
[self.tableView reloadData];
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.title = #"Goals";
self.navigationItem.leftBarButtonItem = self.editButtonItem;
UIBarButtonItem *refreshButton = [[UIBarButtonItem alloc]
initWithBarButtonSystemItem:UIBarButtonSystemItemRefresh
target:self
action:#selector(refresh)];
self.navigationItem.rightBarButtonItem = refreshButton;
[refreshButton release];
[self refresh];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"GoalCellId";
UITableViewCell *cell =
[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1
reuseIdentifier:CellIdentifier] autorelease];
}
Goal *goal = [goals objectAtIndex:indexPath.row];
cell.textLabel.text = goal.name;
cell.detailTextLabel.text = goal.amount;
return cell;
}
goal.name and goal.amount are Null..
This may not be part of your issue, but you should be calling [self init] (or more importantly, [super init] via inheritance):
- (id)initWithDictionary:(NSDictionary *)dictionary {
if (self = [self init]) {
self.name = [dictionary valueForKey:#"name"];
self.amount = [NSString stringWithFormat:#"%#",
[dictionary valueForKey:#"amount"]];
self.goalId = [dictionary valueForKey:#"id"];
self.createdAt = [dictionary valueForKey:#"created_at"];
self.updatedAt = [dictionary valueForKey:#"updated_at"];
}
return self;
}
Also:
for (NSDictionary *dictionary in results) {
Goal *goal = [[Goal alloc] initWithDictionary:
[dictionary objectForKey:#"goal"]];
[goals addObject:goal];
[goal release];
}
Key change is [dictionary objectForKey:#"goal"] in line 3.
The JSON array is of objects with a single goal member, with the properties that your initWithDictionary method is looking for.

Resources