How to handle NSNull values - ios

In Objective C, a common practice is to often forgo checking for nil, instead relying on the fact that messaging nil silently fails.
For example, to validate a string:
if ([myString length] > 1) { // Short and sweet
If myString is nil, this check will fail - as intended. It's a huge improvement over what one may assume would be the correct implementation, explicitly checking for nil:
if (myString && [myString length] > 1) { // Redundant nil check
However, particularly in the case of a web API, myString could conceivable equal [NSNull null] - similar to nil, but with different behavior. Of primary concern, is that messaging NSNull causes an exception. For example, our short simple solution from earlier will cause an exception:
// Causes an exception, because [NSNull null] is an object
NSString *myString = (id)[NSNull null];
if ([myString length] > 1) {
To further complicate things, a simple nil check will actually pass because [NSNull null] is a valid object:
NSString *myString = (id)[NSNull null];
if (myString) { // Evals to YES
So, the only way to be completely safe is to both check for NSNull and do your regular implicit nil check, by calling a method:
if (myString != (id)[NSNull null] && [myString length] > 1) { // Short and sweet
My Question: is this really necessary? Is there a more concise option with less duplicate code, that I've overlooked? Are my conclusions, in fact, correct?

NSNull objects typically crop up with people reading JSON files containing null values.
In that case, there is a chance that the server that supplied the null value thinks you should do something different than with no value. For example, if you get a dictionary and there might be a string stored under some key, you might get a string with non-zero length, you might get an empty string because the server sent you an empty string, you might get nothing because the server sent you nothing, or you might get [NSNull null] because the server sent you a null value. Up to you to decide if the empty string, nothing, and [NSNull null] need to be treated differently or not.
By the way: I have always used some extensions to the NSDictionary class, for example - (NSString*)stringForKey, which will return either an NSString or nil, 100% guaranteed. If you do that, all your error checking is in one method. And you can do things like converting NSNumber to NSString, or NSNull to nil, or NSDictionary etc. to nil, and never worry about it again. Of course you need to check if it is appropriate.

is this really necessary?
If some API can return NSNull instead of a string, then yes, you should check for it. It's usually not necessary since NSNull is not a string and won't be returned by most methods that would return a string. But a collection such as a dictionary, for example, can have any object as a value. If you're not sure that the value will be a string, you should take appropriate precautions.
Is there a better way to handle the possibility of NSNull that I've overlooked?
Another way that you could do roughly the same thing but protect yourself from other types (NSNumber, for example) is to check the type of the thing that you get back rather than comparing specifically against [NSNull null]:
if ([myString isKindOfClass:[NSString class]] && [myString length] > 1) { //...

In addition to the other answers, you could consider replacing NSNull instances with empty strings before parsing your API response into model objects. Similarly, you could traverse a mutable copy of the response dictionary and remove keys whose values contain NSNull.
Basically, do it up front when you get the response so that your code isn't littered with NSNull checks.

Another way to approach this is to use a Category:
#interface NSNull (Utilities)
- (NSString *)stringValue;
#end
#implementation NSNull (Utilities)
- (NSString *)stringValue
{
return nil;
}
#end
And then simply:
[myDictionary[#"key"] stringValue] // returns nil for NSNull values

Related

iOS: Is this a good way to check if JSON dictionary object is an NSString?

I want to check if a JSON object is an NSString and if it isn't, assign it a default string. My ultimate goal is to prevent crashing and assign the properties a proper value no matter what. This is an example of a data model I am using where dict is the JSON dictionary the API returns.
Data *data = [[self alloc] init];
data.name = [NSString validateString:dict[#"name"] defaultString:#""];
data.status = [NSString validateString:dict[#"status"] defaultString:#"OPEN"];
Here is the category method validateString I am using.
+ (NSString *)validateString:(NSString *)aString defaultString:(NSString *)defaultString {
if ([aString isKindOfClass:[NSString class]]) {
return aString;
}
return defaultString;
}
It makes no sense, and is very bad practice, to cast (NSString *)aString and then ask if this is in fact an NSString.
Also, what if it is nil?
All you know when you fetch from a dictionary is that you get an id. Do not assume more than that.
I would suggest writing very plainly: say what you mean, and mean what you say. That is the best practice in Objective-C. Otherwise, dynamic typing and "nil trickery" can lead you into subtle errors. You might not have any trouble in this particular case, but bad habits are bad habits, and it is best not to let them form in the first place. I'd rewrite like this:
+ (NSString *) checkType:(nullable id)obj defaultString:(NSString *)def {
if (obj == nil || ![obj isKindOfClass:[NSString class]]) {
return def;
}
return obj;
}
Like mentioned in other comments: if you want to prevent crashes, you also need to check if it's nil, specially if there is a chance to port your code to Swift in the future.
Just to clarify my last sentence, the line below works in Objective-C even if aString is nil:
if ([aString isKindOfClass:[NSString class]]) {
That's because, in the way Objective-C was made, calling a function on a nil object returns nil, so the if will be considered false, and the function will return defaultString. Yeah... that's certainly a bad idea when they created Objetive-C, since this leads to lots of errors. More details about that behaviour below:
https://stackoverflow.com/a/2696909
Anyway, it's also a good practice to only cast an object after checking its type, so I would recommend adapting your function to this:
+ (NSString *)validateString:(id)obj defaultString:(NSString *)defaultString {
if (obj != nil && [obj isKindOfClass:[NSString class]]) {
return (NSString*)obj;
}
return defaultString;
}
Every object that implements NSObject* has isKindOfClass: (and NSDictionary* only stores objects that implement NSObject*), so we don't need to check if the object responds to it. Also, even if we wanted, respondsToSelector: is also an NSObject* function.
Still, the method that you are using still works. The revised function above is just adapted to better practices and to avoid problems in case you ever need to port this code to Swift (or any other language) in the future.
EDIT: Updated code based in #matt's suggestion.

Objective C syntax issue related to parameter not getting right data

I've been working on a project implementing Hola CDN framework. I'm now running into an issue that I can't pass on current date to the parameter programDay and I traced the source code and I found the below line. What does this below line mean?
self.programDay = ![dict[GETPROGRAMLISTDATA_PROGRAMEDAY] isEqual:[NSNull null]] ? dict[GETPROGRAMLISTDATA_PROGRAMEDAY] : nil;
What's NSNull
[NSNull null] doesn't equal to nil. It means empty value. For example,
#[[NSNull null]].count equals to 1. NSNull is used as placeholder in NSArray and NSDictionary. It means nil.
For your question
This line is used to replace NSNull with nil.
self.programDay = ![dict[GETPROGRAMLISTDATA_PROGRAMEDAY] isEqual:[NSNull null]] ? dict[GETPROGRAMLISTDATA_PROGRAMEDAY] : nil;
This line means that if [dict[GETPROGRAMLISTDATA_PROGRAMEDAY] has empty value or null value the nil would be assign to self.programDay as there would be value of [dict[GETPROGRAMLISTDATA_PROGRAMEDAY] will be assign to self.programDay. Usually (null) value is return from webservice if that value is not presented in database on server.

Checking if a key in standardUserDefaults has been set or is NSNull

What is the correct way to check the contents of standardUserDefaults ?
If I print out what is stored in a key i get:
NSLog(#"logged_in_status : %#", [standardUserDefaults stringForKey:#"logged_in_status"]);
Output: logged_in_status : (null)
So it seems to be NSNull.
So I will add a check for that as follows:
if ([[standardUserDefaults objectForKey:#"logged_in_status"] isKindOfClass:[NSNull class]])
{
NSLog(#"YES");
}
else
{
NSLog(#"NO");
}
Output: NO
Why does this not resolve to the YES? Isn't it an NSNull?
I've been looking at this for a long time and can't see where the error is.
Could anybody kindly explain where I've gone wrong on this?
Thanks,
Code
When you do the key lookup initially, you're not getting back an instance of NSNull, but instead no result at all: in other words, nil, which isn't the same thing as NSNull. That typically means that no value has been set for that key in the user defaults yet. (You can see this if you look at it under the debugger while stepping through-- the "stringified" version of nil happens to be "(null)" when you print it to the log, which is partly what I think may be confusing here.)
(If this distinction is fuzzy for you, have a look at the answer to this question about the differences between the various forms of "null" you might encounter: What are the differences between nil, NULL and [NSNULL nil]?)
As #Francescu suggests in a comment, if you're trying to do a check for "has this value ever been set", it could look something like:
NSString * loggedInStatus = [standardUserDefaults stringForKey:#"logged_in_status"];
if (!loggedInStatus) {
NSLog(#"status not set");
} else {
NSLog(#"status: %#", loggedInStatus);
}
Just some additional info to the other answers: when you format a string including the %# format specifier, a nil value will format as "(null)" with parentheses. An instance of NSNull — the instance, since it's a singleton — formats as "<null>" with angle brackets.

Kind of null variable IOS

i am actually debugging my iOS Application: i want to test if a variable is null or not:
Stupid question huh !
if (var !=nil) { doSomeWork ; }
So if the variable var is equal to nil and we want to print this result in the debugger area we will have something like that:
2012-10-12 21:33:01.553 Application's Name [892:13d03] (null)
This is cool, but indeed when i try to print the variable content in the debugger area, it has been showing :
2012-10-12 21:33:01.553 Application's Name [892:13d03] < null >
Can you tell me guys what is the difference between this two kinds of null, and how can i test if the second one is equal to nil.
Thanks in advance
The second output, <null>, comes from the NSNull singleton. This is a class called NSNull that has a class method +null that returns the same singleton instance of NSNull every time. The primary purpose of this class it to be able to act as a stand-in for nil in places where you can't put nil, such as in collections. For example, JSON libraries typically return NSNull when the JSON includes null.
You can test for this simply by asking if it's == to [NSNull null] (since it's a singleton), or possibly if [obj isKindOfClass:[NSNull null]]. You could use [obj isEqual:[NSNull null]] if you like. You could even ask if it's == kCFNull if you want, since CFNull and NSNull are toll-free bridged. Whatever style you want is up to you.
NSNull it is class, nil is not. So if you are comparing nil with something you should use "==", if NSNull then -> if ([var isEqual:[NSNull null]]) { ....}

iOS, JSON-RPC and NSJSONSerialization: Dealing with null values

I am using JSON-RPC to communicate between iOS application and the server. Some return values of the server are optional.
Considering the combination of technologies I am using, is it better to return these null values like {"telephone": null} or by completely omitting the "telephone" element in the response?
Futher explanation of what I am asking:
It doesn't look like the JSON-RPC spec specifies much to do with the method result (please correct me if I'm wrong) and clearly not sending loads null elements would improve performance and reduce bandwidth somewhat. What I'm most interested in though is the best approach from an iOS NSJSONSerialization perspective. Is it easier/better to check for whether a key exists in an NSDictionary or whether an existing key has a null value?
I'm using a completely different technique, somewhat unorthodox for sure, since my code is dealing with huge data sets and I really hate having to make tests for NSNull on each value. What I did was subclass NSNull, so when dealing with data, I can test if its numeric value is 0, if its string length is 0, etc.
#implementation NSNull (JSON)
- (NSUInteger)length { return 0; }
- (NSInteger)integerValue { return 0; };
- (CGFloat)floatValue { return 0; };
- (NSString *)description { return #"0(null)"; } // so I know it was NSNull in log messages
- (NSArray *)componentsSeparatedByString:(NSString *)separator { return #[]; }
- (id)objectForKey:(id)key { return nil; }
- (BOOL)boolValue { return NO; }
#end
EDIT: I used this exact same code in an e-commerce shipping app. There were literally thousands of objects getting returned by tens of different APIs - having to look at each item to see if it was [NSNull null] would have been a nightmare. I tried to write a routine to groom results, it would recursively look at dictionaries and arrays and reconstruct the object, but it got too complicated.
In any case, I never even once had an issue with this solution. Obviously YMMV.
First of all {"telephone": "null"} is not a null value. There are quotes so actually telephone property has string value with text "null". Server needs to send {"telephone": null} - without quotes.
I'd go with listing all relevant properties that your app needs in response and put null as a value if there is not value. Then you can easily check in NSDictionary you get from NSJSONSerialization if value for key is NSNull.
if ([dictionaryValue isKindOfClass:[NSNull class]])
myThink.myProperty = nil;
I've packed the check in a function, if anyone interested.
-(id)checkNull:(id)object{
if([object isKindOfClass:[NSNull class]])return nil;
else return object;
}

Resources