null, nill is string type in Objective-C - objective-c

//result: ok fine
NSString *email = [dataDic objectForKey:#"email"];
if([email isEqualToString:#"null"])
email = nil;
if((![email length]) == 0)
self.emailLbl.text = email;
// result: not fine
NSString *email = [dataDic objectForKey:#"email"];
if((![email length]) == 0 || (email != nil) )
self.emailLbl.text = email;
In dictionary, email property value contained null value. My question is what is the type of nil? -> if comparison email == nil || email == [NSNull null] ?

There are several values that are different from Objective-C perspective here:
nil (aka null in other languages)
[NSNull null] (a special marker value object)
#"" (empty string)
#"null" (just a string with 4 characters)
If you write your dataDic from your app, and you know that your app handles it well, you don't have to check all the cases. Check only the ones you expect. For example, if your app only writes non-empty strings to the dictionary, but sometimes "email" is not there, you only have to check nil, because objectForKey returns nil if the value is not inside the dictionary.
On the other hand if you have obtained dataDic from a 3rd party API, decoded from JSON for example, then you should do the full checking:
[NSNull null] is placed inside the dictionary if JSON has null originally like {"email":null}
If the server API changed you might get some other structure than NSString there (although quite unlikely here).
nil is returned if you don't have the key/value at all.
You can rule out all the 3 checks at once by doing:
NSString *emailStr = nil;
id emailObj = [dataDic objectForKey:#"email"];
if ([emailObj isKindOfClass:[NSString class]]) {
emailStr = emailObj;
}
Note that you might not have to check for an empty string or nil before assigning to UILabel text, because those work fine and just erase the label text:
self.emailLbl.text = #"";
self.emailLbl.text = nil; // another way to erase

You should check whether the key #"email" contain any value or not like (if its a String)
NSString *email=dic[#"email"];
if (email) {
//do anything with email
}
if you want to check your object is Nil Or Null then do
if (!email || email == (id)[NSNull null]) {
//email unavailable
}
Alternately you can call [Obj isKindOfClass:[NSNull class]] on any object.

Related

null check for dictionary object before call to intvalue still leads to intvalue calls on null object

I get an array of dictionaries back from reading json off a web server and use the following to make sure I got a particular key in the first dictionary in the array before getting its int value:
if([jsonObject[0] objectForKey:#"votes"]!= nil)
{
int votes = [[jsonObject[0] objectForKey:#"votes"] intValue];
[[UserObject userUnique] updateVotes:votes];
}
However, my app still occasionally crashes saying I have called intValue on Null. I have also tried structuring the control statement as
if([jsonObject[0] objectForKey:#"votes"])
but this also leads to the same error/app crashing. My syntax seems in line with accepted answers on SO (Check if key exists in NSDictionary is null or not). Any suggestions for what else/how else I should check the existence of key-value pair for applying intvalue?
Thank you for any advice.
There is a difference between nil and null. nil is not an object: it's a special pointer value. null (as retuned by [NSNull null]) is an object: it's needed because it can be stored in containers like NSDictionary.
NSString *votesString = [jsonObject[0] objectForKey:#"votes"];
if (votesString != nil && votesString != [NSNull null])
{
int votes = [votesString intValue];
[[UserObject userUnique] updateVotes:votes];
}
EDIT: An answer to #SunnysideProductions question
The post you mentioned recommends a way of turning null values into nil values by creating a -safeObjectForKey: method. You are not using -safeObjectForKey:, you are using the default -objectForKey: method.
Be consecutive in your code. Don't run with methods. It would be better add more null- and type-checks in particular in working with json. Let's do it:
if (jsonObject && [jsonObject isKindOfClass:[NSArray class]])
{
NSArray *jsonArray=(NSArray *)jsonObject;
if (jsonArray.count>0)
{
id firstObject=jsonArray[0];
if ([firstObject isKindOfClass:[NSDictionary class]])
{
NSDictionary *jsonDict=(NSDictionary *)firstObject;
id votesNumber=jsonDict[#"votes"];
if (votesNumber && [votesNumber isKindOfClass:[NSNumber class]])
{
int votes=[votesNumber intValue];
[[UserObject userUnique] updateVotes:votes];
}
}
}
}
Now the code is more safe. Does it still crash?
When you call objectForKeyin nullable dictionary, app gets crashed so I fixed this from following way.
- (instancetype)initWithDictionary:(NSDictionary*)dictionary {
id object = dictionary;
if (dictionary && (object != [NSNull null])) {
self.name = [dictionary objectForKey:#"name"];
self.age = [dictionary objectForKey:#"age"];
}
return self;
}

how to check for null object returned in xcode

I have a NULL object returned from a JSON query string and I don't know how to check for it in an If statement. My syntax is below but I still don't seem to be able to trap for the NULL class (i.e., if nothing is returned then no text variable can be set therefore it MUST be a NULL class?), anyway, I need to check that the #"BillingStreet" has something in it and if not to avoid processing it (else the app crashes as it tries to set nothing to the text value of one of the fields in the VC):
- (void) tableView: (UITableView *)itemTableView didSelectRowAtIndexPath: (NSIndexPath *)indexPath{
NSDictionary *obj = [self.dataCustomerDetailRows objectAtIndex:indexPath.row];
NSString *text = [obj objectForKey:#"BillingStreet"] == nil ? #"0" : [obj objectForKey:#"BillingStreet"];
NSLog(#"%#",text);
if (text.class == NULL){
} else {
NSLog(#"no street");
self.labelCustomerAddress.text = [obj objectForKey:#"BillingStreet"];
}
}
A JSON "null" value is converted to [NSNull null], which you can check
for with
if (text == [NSNull null]) ...
because it is a singleton.
Alternatively, you can check if the object contains the expected type, i.e. a string:
NSString *text = [obj objectForKey:#"BillingStreet"];
if ([text isKindOfClass:[NSString class]]) {
self.labelCustomerAddress.text = text;
} else {
self.labelCustomerAddress.text = #"no street";
}
This is more robust in the case that a server sends bad data, e.g. a number or an array instead of a string.
text == nil ? #"0" : text
or
text ? text : #"0"
But if you get it from JSON then you may get instance of NSNull class. In this case you should check
text && ![text isKindOfClass:[NSNull class]] ? text : #"0"

Objective C NSPredicate predicateWithBlock removing nil/null values

I am trying to populate an array by taking an existing array and removing nil values from it. The array was populated from a the JSON response of an http call. Sometimes the array has a null value at the end, and the easiest way to remove that value so I wouldn't have to handle it everywhere in my code would be to use NSArray's filteredArrayUsingPredicate: to assign the variable into the instance variable I use throughout my class.
NSArray *respAgencyList = (NSArray*) [JSON valueForKeyPath:#"xml.path.to.data" ];
NSLog(#"data before filter: %#", respAgencyList);
// prints: ( { domain: "foo.com", name:"foobar"}, "<null>" });
if (respAgencyList != nil && respAgencyList.count > 0) {
agencies = [respAgencyList filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings) {
NSLog(#"Evaluated object is %#", evaluatedObject); //prints <null> for the null value
BOOL ret = evaluatedObject != nil;
return ret;
}]];
}
In the above code the return value is always YES. However, when I put the debugger on and step through it I see:
evaluatedObject = id 0x00000000
Isn't this a null/nil value? What is different about this value compared to nil?
You should also check for NSNull, which can be placed into an NSArray since it is a proper object.
BOOL ret = (evaluatedObject != nil && [evaluatedObject isKindOfClass:[NSNull class]] == NO);
It is impossible for an NSArray to contain a nil element.
Some enumeration methods do hand you nil after the enumeration, as a signal that you've reached the end, but the nil is not in the array — it's just a signal, and you are not expected to do anything serious with it. However, I do not know whether this is one of them.
I suggest that instead of trying to remove nil from the array, which is impossible since nil was never there in the first place, you examine the array directly (log it, look in the debugger, whatever) and assure yourself that what you're trying to do is unnecessary.

How to check if NSString returned by objectForKey is "" objective c

I'm not exactly sure how to check whether a NSString is blank or not, I've got this code...
NSString *imageName = [myItem objectForKey:#"iconName"];
if(imageName == #"")
{
}
And when I do a print on the myItem object, it comes up as..
iconName = "";
At the NSString *imageName line, I noticed in xcode in the console it says
"variable is not NSString"
Which I don't get as iconName is saved and stored on the parse.com database as a NSString.
When I run that code though it doesn't seem to realise that imageName = "";
You should use this code block when comparing strings:
if ([imageName isEqualToString:#""]){
}
You need to use isEqualToString to compare two strings. If you just use == then you are comparing two pointers.
You could also check to see if the object you are receiving is a NSString by:
if ([imageName isKindOfClass:[NSString class]])
Hope this helps.
Although you have a few answers already, here is my take.
First of all, your warning (not error) can be fixed like this:
NSString *imageName = (NSString *)[myItem objectForKey:#"iconName"];
Then, I would check to make sure that the string is not nil and that it is not blank. The easiest way to do this in objective-C is to check the length of the string, since if it nil it will return 0, and if it is empty, it will return 0:
if([imageName length] == 0)
{
// This is an empty string.
}
As #jlehr points out, if there is the possibility that imageName may not actually be stored as a string, then in order to prevent a crash you need to check first. (This may or may not be needed, depending on the logic of your application):
if ([imageName isKindOfClass:[NSString class]]
{
if([imageName length] == 0)
{
// This is an empty string.
}
}
The "variable is not NSString" is probably because objectForKey: return an id.
To should use [imageName isEqualToString:#""].

Check if property of object instance is 'blank'

I am trying to implement the code below without success. Basically, I want to set the display name to use thisPhoto.userFullName if it is not 'Blank", else show thisPhoto.userName instead.
UILabel *thisUserNameLabel = (UILabel *)[cell.contentView viewWithTag:kUserNameValueTag];
NSLog(#"user full name %#",thisPhoto.userFullName);
NSLog(#"user name %#",thisPhoto.userName);
if (thisPhoto.userFullName && ![thisPhoto.userFullName isEqual:[NSNull null]] )
{
thisUserNameLabel.text = [NSString stringWithFormat:#"%#",thisPhoto.userFullName];
}
else if (thisPhoto.userFullName == #"")
{
thisUserNameLabel.text = [NSString stringWithFormat:#"%#",thisPhoto.userName];
}
Currently, even if userFullName is blank, my userName is still not displayed on the screen.
I'd prefer
if([thisPhoto.userFullName length])
Use -length. This will be 0 whenever the string is nil or the empty string #"". You generally want to treat both cases identically.
NSString *fullName = [thisPhoto userFullName];
thisUserNameLabel.text = [fullName length]? fullName : [thisPhoto userName];
I see a few points here
First - if your userFullName instance variable is NSString* then doing simple comparison with nil is enough:
if (thisPhoto.userFullName)
Unless, of course, you explicitly set it to be [NSNull null], which then requires the condition you wrote.
Second - comparing strings is done with isEqualToString: method so second condition should be rewritten as:
if ([thisPhoto.userFullName isEqualToString:#""]) {
...
}
Third - there's logic flaw - If your userFullName IS equal to empty string (#"") the code would still fall to the first branch. I.e. empty string (#"") is not equal to [NSNull null] or simple nil. Hence you should write to branches - one to handle empty string and nil, other one for normal value. So with a bit of refactoring your code becomes like this:
thisUserNameLabel.text = [NSString stringWithFormat:#"%#",thisPhoto.userFullName];
if (!thisPhoto.userFullName || [thisPhoto.userFullName isEqualToString:#""]) {
// do the empty string dance in case of empty userFullName.
}
If, as I suppose, thisPhoto.userFullName is a NSString you may try
[thisPhoto.userFullName isEqualToString:#""]
The other two answers are correct, and beat me to it. Rather than just repeat what they have said - I'll point out something else.
[NSNull null] is used to store nil values in collection classes (NSArray, NSSet, NSDictionary) that don't allow nil values to be stored in them.
So unless you're checking values that you get from a collection - there is no point checking against [NSNull null]
// this assumes userFullName and userName are strings and that userName is not nil
thisUserNameLabel.text = [thisPhoto.userFullName length] > 0 ? thisPhoto.userFullName : thisPhoto.userName;
"Blank" means #"", but also #" " or #"\n". So I would trim userFullName and check the length of that string.
if ([[thisPhoto.userFullName stringByTrimmingCharactersInSet:
[NSCharacterSet whitespaceAndNewlineCharacterSet]] length] == 0) {
// it's blank!
}