Tableview crashing when JSON returns null ios - ios

I am using the google places api to search for nearby restaurants. Each restaurant is displayed in a cell in a UITableView. The return response is in the JSON format. Of the the things the JSON contains is a decimal number that represents the rating of the restaurant. I want to display the rating in the cells. However sometimes there is no rating for the restaurant and the value is (null). So I added a check in the cellForRowAtIndexPath method that checks if the value of the rating is null or not.
if([dict valueForKey:kResponseRating] != [NSNull null])
{
NSNumber *rating = [dict valueForKey:kResponseRating];
[cell displayRating:rating];
}
else
{
NSLog(#"Value of rating is null");
}
When I run the application the tableView still crashes as soon as a null value is returned and the string "Value of rating is null" is NOT printed. So its not going into the else statement even tho the value in the json is null.
Ok so I checked if the return value is of type NSString class and its not. Here is what i did in the same method:
if([[dict objectForKey:kResponseRating] isKindOfClass:[NSString class]])
{
NSLog(#"The return response is of type NSString");
}
And it did not go into if statement.
Here is the method that calculates the rating and posts it. The method takes the rating rounds it to the nearest .5 and then displays it as stars out of 5.
-(void)displayRating:(NSNumber*)rating{
double rate = [rating doubleValue];
rate = round(rate * 2.0) / 2.0;
int counter = 0;
double compareVar = 1.0;
while(counter <= 4){
if(rate == 0.0)
{
imgRating[counter] = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"starempty.png"]];
[self.contentView addSubview:imgRating[counter]];
}
else
{
if(compareVar < rate)
{
imgRating[counter] = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"starfill.png"]];
[self.contentView addSubview:imgRating[counter]];
}
else if(compareVar == rate)
{
imgRating[counter] = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"starfill.png"]];
[self.contentView addSubview:imgRating[counter]];
}
else
{
if(compareVar - rate == 0.5)
{
imgRating[counter] = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"starhalffill.png"]];
[self.contentView addSubview:imgRating[counter]];
}
else
{
imgRating[counter] = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"starempty.png"]];
[self.contentView addSubview:imgRating[counter]];
}
}
counter++;
compareVar = compareVar + 1.0;
}
}
NSLog(#"This is rate: %f", rate);
}
Is a line of code in this method causing a crash?

Try the below check
if(![dict objectForKey:kResponseRating] || [dict objectForKey:kResponseRating] == [NSNull null]) {
NSLog(#"Either the key doesn't exist or the value is null");
}
else {
// Covert and set the rating here!
}

This should do it.
if ([[dictionary valueForKey:#"key"] isKindOfClass:[NSNull class]] || ![dict valueForKey:#"key"]){
// Key doesn't exist or equals <null>
}
else{
// Key exist
}
And I'm pretty sure there are plenty of answers to this question on stack overflow. You should try looking if there is an answers to your question before creating a new one.
Anyway, I hope this helps

Related

UITableView sorting

I've been brought in on this project where the previous developers made custom table cells and headers by using xib files and then registering the nibs like so:
[self.accountTable registerNib:[UINib nibWithNibName:kNonATITableViewCellLandscapeNib bundle:[NSBundle mainBundle]] forCellReuseIdentifier:kNonATITableViewCellLandscapeIdentifier];
[self.accountTable registerNib:[UINib nibWithNibName:kNonATITableHeaderLandscapeNib bundle:[NSBundle mainBundle]] forCellReuseIdentifier:kNonATITableHeaderLandscapeId];
The header files have buttons in them and uiimageviews. The buttons are for sorting, the uiimageviews for an arrow icon to show you the direction of the sort (asc, desc). All the buttons and imageviews are IBOutlets. All the buttons are linked to an IBAction:
- (IBAction)sortButtonTouched:(id)sender;
The file also has two other properties:
#property (nonatomic, assign) SortType currentSortingOption;
#property (nonatomic, strong) UIButton* btnLastTouched;
Here is sortButtonTouched:
- (IBAction)sortButtonTouched: (UIButton*) buttonTouched {
if (!self.btnLastTouched) {
self.btnLastTouched = buttonTouched;
}
NSString* strFieldToSort;
UIImageView* ivSortImage;
NSArray* arrSortIcons = [[NSArray alloc] initWithObjects:self.ivAccountSort,self.ivNameSort, self.ivAddressSort, self.ivCitySort, self.ivZipSort, self.ivLastCallSort, self.ivMileageSort, nil];
//get the image for the button selected
if (buttonTouched.tag == 0) {
strFieldToSort = #"customerNumber";
ivSortImage = self.ivAccountSort;
} else if (buttonTouched.tag == 1) {
strFieldToSort = #"customerName";
ivSortImage = self.ivNameSort;
} else if (buttonTouched.tag == 2) {
strFieldToSort = #"address";
ivSortImage = self.ivAddressSort;
} else if (buttonTouched.tag == 3) {
strFieldToSort = #"city";
ivSortImage = self.ivCitySort;
} else if (buttonTouched.tag == 4) {
strFieldToSort = #"zip";
ivSortImage = self.ivZipSort;
} else if (buttonTouched.tag == 5) {
strFieldToSort = #"lastCallDate";
ivSortImage = self.ivLastCallSort;
} else if (buttonTouched.tag == 6) {
strFieldToSort = #"mileage";
ivSortImage = self.ivMileageSort;
}
//set the sort option and add icon
if (!self.currentSortingOption) {
self.currentSortingOption = SORT_ASC;
[ivSortImage setImage:[UIImage imageNamed:Ascending_Icon]];
} else {
if (![self.btnLastTouched isEqual:buttonTouched]) {
self.currentSortingOption = SORT_ASC;
[ivSortImage setImage:[UIImage imageNamed:Ascending_Icon]];
} else {
if (self.currentSortingOption == SORT_ASC) {
self.currentSortingOption = SORT_DESC;
[ivSortImage setImage:[UIImage imageNamed:Descending_Icon]];
} else {
self.currentSortingOption = SORT_ASC;
[ivSortImage setImage:[UIImage imageNamed:Ascending_Icon]];
}
}
}
//show and hide
for(int i=0; i<arrSortIcons.count; i++) {
UIImageView* ivThisImage = [arrSortIcons objectAtIndex:i];
if (buttonTouched.tag == i) {
[UIView animateWithDuration:.25 animations:^(void) {
ivThisImage.alpha = 1.0;
}];
} else {
[UIView animateWithDuration:.25 animations:^(void) {
ivThisImage.alpha = 0.0;
}];
}
}
//call back to routing view controller and sort results based on sort order and field selected
NSDictionary* dictUserData = [[NSDictionary alloc] initWithObjectsAndKeys:
#"Sort Non-ATI", #"Action",
strFieldToSort, #"Field To Sort",
[NSNumber numberWithLong:self.currentSortingOption], #"Sortng Option",
nil];
[[NSNotificationCenter defaultCenter] postNotificationName:#"rvc" object:self userInfo:dictUserData];
self.btnLastTouched = buttonTouched;
}
And the notification fires this method:
- (void) sortNonATIResults : (NSDictionary*) dictSortParams {
if (self.arrNonATIResults.count > 0) {
NSString* sortKey = [dictSortParams objectForKey:#"Field To Sort"];
//change the field to sort to match the customerInfo object properties...
NSNumber* numSortType = [dictSortParams objectForKey:#"Sortng Option"];
BOOL isAsc = YES;
if ([numSortType intValue] == 2) {
isAsc = NO;
}
NSSortDescriptor* sortDescriptor = [[NSSortDescriptor alloc] initWithKey:sortKey ascending:isAsc];
NSArray* arrSortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
NSArray* arrSortedNonATIResults = (NSArray*)[self.arrNonATIResults sortedArrayUsingDescriptors:arrSortDescriptors];
self.arrNonATIResults = [arrSortedNonATIResults mutableCopy];
self.arrDatasource = self.arrNonATIResults;
[self.accountTable reloadData];
}
}
There are two problems right now. The icons are not showing up if the notification is sent. Comment out the notification and they function as expected. The other problem is that the property currentSortingOption doesn't retain it's value. I think both issues are related but I am not 100% sure. When the tableview is reloaded, does the header get instantiated again? This would make sense to me since then the uiimageviews would be reset with no image and the property would lose it's value and reset to 0 (it is the value of a typedef).
So, I am correct, how can I resolve this and if not, what could be causing the problems?
Thanks
OK, sorry for posting and then solving my problem right away, I guess sometimes you just need to write out the problem to find the solution. All I needed to do was not reload the table but just reload the rows. Here's the updated method:
(void) sortNonATIResults : (NSDictionary*) dictSortParams {
if (self.arrNonATIResults.count > 0) {
NSString* sortKey = [dictSortParams objectForKey:#"Field To Sort"];
//change the field to sort to match the customerInfo object properties...
NSNumber* numSortType = [dictSortParams objectForKey:#"Sortng Option"];
BOOL isAsc = YES;
if ([numSortType intValue] == 2) {
isAsc = NO;
}
NSSortDescriptor* sortDescriptor = [[NSSortDescriptor alloc] initWithKey:sortKey ascending:isAsc];
NSArray* arrSortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
NSArray* arrSortedNonATIResults = (NSArray*)[self.arrNonATIResults sortedArrayUsingDescriptors:arrSortDescriptors];
self.arrNonATIResults = [arrSortedNonATIResults mutableCopy];
self.arrDatasource = self.arrNonATIResults;
dispatch_async(dispatch_get_main_queue(), ^{
NSMutableArray *indexPathArray = [[NSMutableArray alloc] init];
for (NSInteger section = 0; section < [self.accountTable numberOfSections]; ++section)
{
for (NSInteger row = 0; row < [self.accountTable numberOfRowsInSection:section]; ++row)
{
[indexPathArray addObject:[NSIndexPath indexPathForRow:row inSection:section]];
}
}
[self.accountTable reloadRowsAtIndexPaths:indexPathArray withRowAnimation:UITableViewRowAnimationNone];
[self.accountTable scrollsToTop];
});
}
}

Button selection on BOOL UIButton

I am attempting to show a button in the selected state when based on a boolean off of JSON. Here is my code for my custom UITableViewCell:
TwitterCell *cell = [self.tableView dequeueReusableCellWithIdentifier:#"TwitterCell"];
if (cell == nil) {
cell = [[TwitterCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"TwitterCell"];
}
NSDictionary *data = tweets[indexPath.row];
id favorited = data[#"favorited"];
NSLog(#"%#",favorited);
id retweeted = data[#"retweeted"];
NSLog(#"%#",retweeted);
if ((int)favorited == 1) {
[cell.favoriteButton select];
} else {
[cell.favoriteButton deselect];
}
if ((int)retweeted == 1) {
[cell.retweetButton select];
} else {
[cell.retweetButton deselect];
}
Here is a possible point of issue in my custom TableViewCell class. Where I prepare for reuse:
- (void)prepareForReuse
{
[super prepareForReuse];
self.favoriteButton.selected = NO;
self.tweetLabel.text = nil;
self.profilePicture.image = nil;
self.nameLabel.text = nil;
self.retweetButton.selected = NO;
self.usernameLabel.text = nil;
}
There are a couple of things wrong with the code you have provided.
1 - Casting NSNumber to int
Retrieving favorited, which is presumably a number, from the dictionary will return an NSNumber which is a wrapper class for numbers. When you NSLog this it will print the result of it's description method, essentially printing out the underlying number.
When you cast an NSNumber to int you don't get the underlying number but the memory address in integer format.
See this code and some example output:
NSNumber *favorited = #(1);
NSLog(#"%#", favorited);
NSLog(#"%d", (int)favorited);
favorited = #(0);
NSLog(#"%#", favorited);
NSLog(#"%d", (int)favorited);
Outputs
2015-08-19 07:04:36.235 xctest[589:7545] 1
2015-08-19 07:04:36.236 xctest[589:7545] 18
2015-08-19 07:04:36.236 xctest[589:7545] 0
2015-08-19 07:04:50.720 xctest[589:7545] 2
To retrieve the underlying number it is necessary to use one of the appropriate *value methods such as integerValue or longValue.
2 - Calling select and deselect
Neither of these methods exist on UIButton unless you have created a custom subclass. To properly adjust the selected state of a button you need to use the selected property as you have done in the prepareForReuse method.
button.selected = YES; // or NO
Putting it all together
Taking the above information we can change part of the cell creation method as follows:
if ([favorited integerValue] == 1) {
cell.favoriteButton.selected = YES;
} else {
cell.favoriteButton.selected = NO;
}
if ([retweeted integerValue] == 1) {
cell.retweetButton.selected = YES;
} else {
cell.retweetButton.selected = NO;
}
I think the issue is with
(int)favorited == 1
Just use [favorited intvalue] things will be fine.

how to check if array is null or empty in ios sdk?

I want to check if my Bond is empty or null and NULL value.
{
Bonds =(
{
DOB = "12/09/1988";
about = "";
}
);
User = {
city = CA;
email = "karmhadadmtl#gmail.com";
};
success = True;
}
Second time this type get data how to check Bond key
{
Bonds = Null;
User = {
city = CA;
email = "developer.i12#gmail.com";
};
success = True;
}
You just check for nil
if(data[#"Bonds"]==nil){
NSLog(#"it is nil");
}
or
if ([data[#"Bonds"] isKindOfClass:[NSNull class]]) {
NSLog(#"it is null");
}
if ([data[#"Bonds"] isKindOfClass:[NSNull class]] || data[#"Bonds"] == nil || [data[#"Bonds"] isEqualToString:#""]) {
}
[NSNull null] always returns a same object so this should work fine.
if (dictionary[#"Bonds"] != [NSNull null]) {
// requried data is present, now check if it is nil or empty.
}
check: if (![dataArray isKindOfClass:[NSNull class]]) &&
check if it having elements [dataArray firstObject] - to check array having one or more elements.
The best possible way should be:
if (!data[#"Bonds"] || ![data[#"Bonds"] count]){
NSLog(#"Data Found");
}else{
NSLog(#"Data Not Found");
}

NSMutable Array crashed when being accessed at other method

-(void)Aray
{
NSMutableArray *ColorArray = [[NSMutableArray alloc] init];
if(Counter < NewColor)
{
[ColorArray addObject:[NSNumber numberWithInteger:ColorTemp]];
Counter += 1;
}
}
-(IBAction)Go:(id)sender
{
NSMutableArray *ColorArray = [[NSMutableArray alloc] init];
Color = [[ColorArray objectAtIndex:Index] intValue];
if(Color == 2)
{
ColorLabel.text = #"The Color is Black";
Screen.image = [UIImage imageNamed:#"BlackTile.png"];
}
else
{
Screen.image = [UIImage imageNamed:#"Tunnel.png"];
ColorLabel.text = #"The Color is Green";
}
Index += 1;
}
-(IBAction)Black:(id)sender
{
ColorTemp = 2;
NewColor += 1;
[self Array];
}
-(IBAction)Green:(id)sender
{
ColorTemp = 1;
NewColor += 1;
[self Array];
}
The issue is that the ColorArray needs to be an instance variable (or #property) of the class so that it persists outside of the method calls.
This code will always crash, regardless of the value of Index:
NSMutableArray *ColorArray = [[NSMutableArray alloc] init];
Color = [[ColorArray objectAtIndex:Index] intValue];
Color appears to already be an instance variable (or #property), so this concept should not be alien to you.
Side note: variables conventionally start with lower case and use camal-case naming.

I can't figure this crash out: [__NSArrayM insertObject:atIndex:]: object cannot be nil

I am having the [__NSArrayM insertObject:atIndex:]: object cannot be nil crash when I test the app on my device, but not on the iOS simulator.
Here's what going on:
I am managing 5 UITextField on a View Controller, then I am passing the text of each UITextField to a another View Controller via an NSString using an IBAction (when I press the button, it crashes).
TextViewController
- (IBAction)choicebutton:(id)sender {
AnswerViewController *AVC = [self.storyboard instantiateViewControllerWithIdentifier:#"AnswerViewController"];
AVC.stringFromChoice1 = self.choice1.text;
AVC.stringFromChoice2 = self.choice2.text;
AVC.stringFromChoice3 = self.choice3.text;
AVC.stringFromChoice4 = self.choice4.text;
AVC.stringFromChoice5 = self.choice5.text;
[self presentViewController:AVC animated:YES completion:nil];
}
Then on the AnswerViewController, I am creating an NSMutableArray and randomizing the NSStrings to be displayed on a UILabel.
AnswerViewController
- (void)viewDidLoad
{
self.choiceAnswers1 = [[NSMutableArray alloc] initWithCapacity:5];
if(![self.stringFromChoice1 isEqualToString:#""])
{
[self.choiceAnswers1 addObject:self.stringFromChoice1];
}
if(![self.stringFromChoice2 isEqualToString:#""])
{
[self.choiceAnswers1 addObject:self.stringFromChoice2];
}
if(![self.stringFromChoice3 isEqualToString:#""])
{
[self.choiceAnswers1 addObject:self.stringFromChoice3];
}
if(![self.stringFromChoice4 isEqualToString:#""])
{
[self.choiceAnswers1 addObject:self.stringFromChoice4];
}
if(![self.stringFromChoice5 isEqualToString:#""])
{
[self.choiceAnswers1 addObject:self.stringFromChoice5];
}
int index = arc4random() % [self.choiceAnswers1 count];
self.choiceanswer.text = self.choiceAnswers1[index];
self.choiceanswer1.text = self.choiceAnswers1[index];
}
I've set it up this way in case the user doesn't fill all of the UITextFields, does this have to do anything with the crash? I can't figure this one out, please help!
Thanks!
Don’t use compare: against the empty string—it doesn’t catch the case where your string is nil and not #“”. Those are two distinct cases.
Instead of this:
if(![self.stringFromChoice1 isEqualToString:#""])
{
[self.choiceAnswers1 addObject:self.stringFromChoice1];
}
use this:
if (self.stringFromChoice1.length)
[self.choiceAnswers1 addObject:self.stringFromChoice1];
Since in C any non-0 value is true, and since sending a message to a nil object always returns 0, this catches all the cases. And is less wordy.
Less code is better code!
Change you viewDidLoad something like this.
- (void)viewDidLoad
{
self.choiceAnswers1 = [[NSMutableArray alloc] init];
if(self.stringFromChoice1.length > 0)
{
[self.choiceAnswers1 addObject:self.stringFromChoice1];
}
if(self.stringFromChoice2.length > 0)
{
[self.choiceAnswers1 addObject:self.stringFromChoice2];
}
if(self.stringFromChoice3.length > 0)
{
[self.choiceAnswers1 addObject:self.stringFromChoice3];
}
if(self.stringFromChoice4.length > 0)
{
[self.choiceAnswers1 addObject:self.stringFromChoice4];
}
if( self.stringFromChoice5.length > 0)
{
[self.choiceAnswers1 addObject:self.stringFromChoice5];
}
int index = arc4random() % [self.choiceAnswers1 count];
self.choiceanswer.text = self.choiceAnswers1[index];
self.choiceanswer1.text = self.choiceAnswers1[index];
}
Let me know it that helps.. :)

Resources