objectForKey null check - ios

Trying to check for validity of the data in item
(item is NSDictionary)
I thought this should work but I do get into the second if and crash with:
unrecognized selector sent to instance
becuase galleryArr is (null)
NSArray *galleryArr = [item objectForKey:#"photos"];
if (galleryArr != nil ) {
if ([galleryArr count] != 0) {
//do something
}
}
Any ideas?

I've solved this issue with a simple this simple Objective-C category:
NSDictionary+NotNull.h
#import <Foundation/Foundation.h>
/*! This category extends NSDictionary to work around an issue with NSNull object.
*/
#interface NSDictionary (NotNull)
/*! #abstract Returns the value associated with a given key, but only if the value is not NSNull.
#param aKey The key for which to return the corresponding value.
#return The value associated with the given key, or nil if no value is associated with the key, or the value is NSNull.
*/
- (id)objectOrNilForKey:(id)aKey;
#end
NSDictionary+NotNull.m
#import "NSDictionary+NotNull.h"
#implementation NSDictionary (NotNull)
- (id)objectOrNilForKey:(id)aKey
{
id object = [self objectForKey:aKey];
if (object == [NSNull null]) {
return nil;
}
return object;
}
#end
Now you can just call:
NSArray *galleryArr = [item objectOrNilForKey:#"photos"];

Add a check for [gallryArr isKindOfClass:[NSArray class]].

Maybe you get NSNull? That's a singleton ([NSNull null]) object that represent nil. You can check if([gallryArr isKindOfClass:[NSArray class]]).

Related

NSNull integerValue: Unrecognized Selector - Flutter IOS only [duplicate]

I'm developing an iOS 5.0+ app with latest SDK.
I get a very strange error with this code:
- (NSMutableURLRequest*)setupRequestWithService:(NSString*)service andMethod:(NSString*)method
{
NSString* url = [NSString stringWithFormat:#"%#%#.svc/%#", serverUrl, service, method];
NSMutableURLRequest* request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:url]];
// Set authentication token.
NSLog(#"???????????? %#", authenticationToken);
if (authenticationToken == nil)
NSLog(#"NULL AUTHTOKEN");
if ([authenticationToken isEqual:[NSNull null]])
NSLog(#"NSNULL AUTHTOKEN");
if (request == nil)
NSLog(#"NULL REQUEST");
[request addValue:authenticationToken forHTTPHeaderField:REQUEST_HEADER_AUTH_TOKEN];
return request;
}
This is my log:
???????????? <null>
NSNULL AUTHTOKEN
-[NSNull length]: unrecognized selector sent to instance 0x3b5a5090
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSNull length]: unrecognized selector sent to instance 0x3b5a5090'
It seems that authenticationToken is NULL. But I don't understand that, if authenticationToken is NULL why I don't see NULL AUTHTOKEN on the log.
I get this error the second time I run that method, the first time, I don't get any error. This is my log:
???????????? (null)
NULL AUTHTOKEN
By the way:
NSString* authenticationToken;
Any advice?
Maybe there is a Memory Leak somewhere...
My solution to this maddening use of NSNull by JSON interpreters is to create a category on NSNull, where I define integerValue, floatValue, length, etc - return 0 for all. Everytime you get another crash add a new category. I think I had 6 or 7 when I had this issue.
The problem with NOT doing this is you have to look for the NULL everywhere in your converted objects - a PITA in my opinion.
EDIT: the code I'm using, all in a NSNull+JSON.m file:
#interface NSNull (JSON)
#end
#implementation NSNull (JSON)
- (NSUInteger)length { return 0; }
- (NSInteger)integerValue { return 0; };
- (float)floatValue { return 0; };
- (NSString *)description { return #"0(NSNull)"; }
- (NSArray *)componentsSeparatedByString:(NSString *)separator { return #[]; }
- (id)objectForKey:(id)key { return nil; }
- (BOOL)boolValue { return NO; }
#end
EDIT2: Now in Swift 3:
extension NSNull {
func length() -> Int { return 0 }
func integerValue() -> Int { return 0 }
func floatValue() -> Float { return 0 };
open override var description: String { return "0(NSNull)" }
func componentsSeparatedByString(separator: String) -> [AnyObject] { return [AnyObject]() }
func objectForKey(key: AnyObject) -> AnyObject? { return nil }
func boolValue() -> Bool { return false }
}
The error message is pretty clear. NSNull and nil are different things:
The NSNull class defines a singleton object used to represent null values in
collection objects (which don’t allow nil values).
If you want to check if authenticationToken is NSNull try: [authenticationToken isEqual: [NSNull null]]
In line with David H's answer, how about a category on NSNull that just uses ObjC's message forwarding to "do nothing", to emulate the runtime's behavior when sending messages to nil?
Like this:
#interface NSNull (ForwardInvocation)
#end
#implementation NSNull (ForwardInvocation)
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
return [NSNull methodSignatureForSelector:#selector(description)];
}
- (void)forwardInvocation:(NSInvocation *)anInvocation {
// do nothing; prevent 'unrecognized selector' crashes
}
#end
The [NSNull methodSignatureForSelector:#selector(description)]; takes advantage of the fact that NSNull inherits from NSObject, which provides the description method. This satisfies the forwarding mechanism requirement for implementing -methodSignatureForSelector:.
The problem comes because your method return an NSNull object.
You can't check [authenticationToken isEqual:[NSNull null]]) because [NSNull null] give an instance of an object. So it's different from your object itself.
If you want to check if you received an NSNull object you need to check like this: [authenticationToken isKindOfClass:[NSNull class]] instead.

Check if key of NSMutableDictionary is nil

If got a NSMutableDictionary from Json Data
NSMutableDictionary *returnedDict = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error];
I know that this key returnedDict[#"data"][#"weather"][day][#"tides"] is NSNull in some cases. So I get -[NSNull objectForKeyedSubscript:]
So I try according to this answer How to check if an NSDictionary or NSMutableDictionary contains a key? to check if it is nil or not.
if (returnedDict[#"data"][#"weather"][day][#"tides"]){ some code }
and
if (returnedDict[#"data"][#"weather"][day][#"tides"]!=[NSNull null]){ some code}
does not avoid to run {some code}
How do I check this in the right way?
So your issue is:
Your server may return null to indicate that an object isn't present. NSJSONSerialization will convert that null into an instance of NSNull. In theory that means that instead of doing result[a][b][c] you need to check whether result[a] is a dictionary and, if so, whether result[a][b] is a dictionary, etc, etc, which is repetitious and error-prone?
Perhaps the easiest thing might be to remove from the dictionary any key with a value of NSNull, so that next time you ask for the value you'll get an ordinary nil, which is safe to message per the usual compound-messaging rules?
NSJSONSerialization won't do that for you but it's easy enough to add after the fact:
#interface NSDictionary(RemoveNullValues)
- (NSDictionary *)ty_collectionWithoutNullValues;
#end
#interface NSArray(RemoveNullValues)
- (NSArray *)ty_collectionWithoutNullValues;
#end
[...]
#implementation NSDictionary(RemoveNullValues)
- (NSDictionary *)ty_collectionWithoutNullValues {
NSMutableDictionary *reducedDictionary = [self mutableCopy];
// remove any keys for which NSNull is the direct value
NSArray *keysEvaluatingToNull = [self allKeysForObject:[NSNull null]];
[reducedDictionary removeObjectsForKeys:keysEvaluatingToNull];
// ask any child dictionaries to do the same; note that it's safe
// to mutate reducedDictionary in this array because allKeys is a
// copy property; what you're iterating is not reducedDictionary
// but a snapshot of its keys when the array first began
for (id key in [reducedDictionary allKeys]) {
id child = reducedDictionary[key];
if ([child respondsToSelector:#selector(ty_collectionWithoutNullValues)]) {
reducedDictionary[key] = [child ty_collectionWithoutNullValues];
}
}
return [reducedDictionary copy];
}
#end
#implementation NSArray(RemoveNullValues)
- (NSArray *)ty_collectionWithoutNullValues {
NSMutableArray *reducedArray = [NSMutableArray array];
for (id child in self) {
if ([child isKindOfClass:[NSNull class]]) continue;
if ([child respondsToSelector:#selector(ty_collectionWithoutNullValues)]) {
[reducedArray addObject:[child ty_collectionWithoutNullValues]];
} else {
[reducedArray addObject:child];
}
}
return [reducedArray copy];
}
#end
You must read this answer in conjunction with the accepted answer and comments to the question Is there NSMutableDictionary literal syntax to remove an element?
Following on from the linked answer you can quietly remove all the NSNull's and return nil instead if you access the element using the literal syntax (i.e. not using objectForKey:) by adding the following to your application:
#implementation NSDictionary (ClobberNSNull)
- (id) objectForKeyedSubscript:(id<NSCopying>)key
{
id result = [self objectForKey:key];
return result == NSNull.null ? nil : result;
}
#end
Now when you use the syntax:
dictionary[key]
if the matching object is NSNull then nil will be returned just as if the key did not exist.
There are caveats, see the linked question, and you need to decide if this approach is suitable for your situation. But it is simple.
HTH
Note: Before someone comments, NSNull is a singleton so the == is OK.
use
if(![returnedDict[#"data"][#"weather"][day][#"tides"] isKindOfClass:[NSNull class]]) { some code }

Resilience in JSON parsing with ObjectiveC

A JSON API feed used by our iOS ObjectiveC app is a bit flaky, so sometimes a field is null.
When parsing JSON we use
NSDictionary *json = [self JSONFromResponseObject:responseObject];
Then try to use the fields with e.g.
[widgetIDArray addObject:widget[#"name"][#"id"]];
Where sometimes the "name" field will be a null. Do we:
1) Ask the API provider to clean up their flaky API code
2) Check for null each and every time we try to use something from the json dict
if ( ![widget[#"name"] isKindOfClass:[NSNull class]] )
3) Use try - catch
#try {
[widgetIDArray addObject:widget[#"name"][#"id"]];
}
#catch (NSException *exception)
{
NSLog(#"Exception %#",exception);
}
ANSWER:
Thanks for the answers, below. Here is the extension to NSObject I added that allows me to get deeply nested JSON items that may or may not be present.
First call with something like
self.item_logo = [self valueFromJSONWithKeyArray:event withKeyArray:#[#"categories",#"bikes",#"wheels",#"model",#"badge_uri"]];
Here is the code in NSObject+extensions.m
- (id) valueFromJSONWithKeyArray:(id)json withKeyArray:(NSArray *)keyArray
{
for (NSString * keyString in keyArray)
{
if ([json[keyString] isKindOfClass:[NSObject class]])
{
json = json[keyString]; // go down a level
}
else
{
return nil; // we didn't find this key
}
}
return json; // We successfully found all the keys, return the object
}
null in a JSON response isn't "flaky", it is absolutely standard.
Even if it was "flaky", any message that you receive from the outside is an attack vector that could allow an attacker to hack into your program, so resilience is required. Crashing when your receive a null allows a DOS attack against your application.
#try / #catch is awful. Exceptions are thrown in response to programming errors. You don't catch them, you fix your code.
How do you fix your code? Simple. Write a few helper methods in an NSDictionary extension.
First you don't know that json is a dictionary. So you add an NSDictionary class method where you pass in anything and it returns what you passed if it is a dictionary and nil (with appropriate logging) if it is anything else.
Next you assume that there is a dictionary under the key "name". So you write an extension "jsonDictionaryForKey" which returns a dictionary if there is one, and nil (with appropriate logging) if it is anything else.
And so on. Make your JSON parsing bullet proof if you want to call yourself a professional developer. For extra bonus points you add a method which will take a dictionary and list all keys that are present that you didn't ask for - so you know if your API is sending things that you don't expect.
You can delete all NSNULL values in your JSON object. Here is a function I used in my library to git rid of all null values in a JSON object.
id BWJSONObjectByRemovingKeysWithNullValues(id json, NSJSONReadingOptions options) {
if ([json isKindOfClass:[NSArray class]]) {
NSMutableArray *mutableArray = [NSMutableArray arrayWithCapacity:[(NSArray *)json count]];
for (id value in (NSArray *)json) {
[mutableArray addObject:BWJSONObjectByRemovingKeysWithNullValues(value, options)];
}
return (options & NSJSONReadingMutableContainers) ? mutableArray : [NSArray arrayWithArray:mutableArray];
} else if ([json isKindOfClass:[NSDictionary class]]) {
NSMutableDictionary *mutableDictionary = [NSMutableDictionary dictionaryWithDictionary:json];
for (id<NSCopying> key in [(NSDictionary *)json allKeys]) {
id value = [(NSDictionary *)json objectForKey:key];
if (isNullValue(value)) {
[mutableDictionary removeObjectForKey:key];
} else if ([value isKindOfClass:[NSArray class]] || [value isKindOfClass:[NSDictionary class]]) {
[mutableDictionary setObject:BWJSONObjectByRemovingKeysWithNullValues(value, options) forKey:key];
}
}
return (options & NSJSONReadingMutableContainers) ? mutableDictionary : [NSDictionary dictionaryWithDictionary:mutableDictionary];
}
return json;
}
After all null values have been cleared, perhaps the exceptions will gone too.
An approach that's often used is categories similar to these on NSDictionary and NSMutableDictionary, that ignore nil and NSNull.
#interface NSDictionary (nullnilsafe)
- (ObjectType)nullnilsafe_objectForKey:(KeyType)aKey;
#end
#implementation NSDictionary
- (ObjectType)nullnilsafe_objectForKey:(KeyType)aKey
{
id obj = [self objectForKey:aKey];
if (obj && ![obj isKindOfClass:[NSNull class]] ){
return obj;
}
return nil;
}
#end
#interface NSMutalbeDictionary (nullnilsafe)
- (void)nullnilsafe_setObject:(ObjectType)anObject forKey:(id<NSCopying>)aKey;
#end
#implementation NSMutalbeDictionary
- (void)nullnilsafe_setObject:(ObjectType)anObject forKey:(id<NSCopying>)aKey
{
if (anObject && aKey && ![anObject isKindOfClass:[NSNull class]]){
[self setObject:anObject forKey:aKey];
}
}
#end

Exception during populating tableView with string [duplicate]

I'm developing an iOS 5.0+ app with latest SDK.
I get a very strange error with this code:
- (NSMutableURLRequest*)setupRequestWithService:(NSString*)service andMethod:(NSString*)method
{
NSString* url = [NSString stringWithFormat:#"%#%#.svc/%#", serverUrl, service, method];
NSMutableURLRequest* request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:url]];
// Set authentication token.
NSLog(#"???????????? %#", authenticationToken);
if (authenticationToken == nil)
NSLog(#"NULL AUTHTOKEN");
if ([authenticationToken isEqual:[NSNull null]])
NSLog(#"NSNULL AUTHTOKEN");
if (request == nil)
NSLog(#"NULL REQUEST");
[request addValue:authenticationToken forHTTPHeaderField:REQUEST_HEADER_AUTH_TOKEN];
return request;
}
This is my log:
???????????? <null>
NSNULL AUTHTOKEN
-[NSNull length]: unrecognized selector sent to instance 0x3b5a5090
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSNull length]: unrecognized selector sent to instance 0x3b5a5090'
It seems that authenticationToken is NULL. But I don't understand that, if authenticationToken is NULL why I don't see NULL AUTHTOKEN on the log.
I get this error the second time I run that method, the first time, I don't get any error. This is my log:
???????????? (null)
NULL AUTHTOKEN
By the way:
NSString* authenticationToken;
Any advice?
Maybe there is a Memory Leak somewhere...
My solution to this maddening use of NSNull by JSON interpreters is to create a category on NSNull, where I define integerValue, floatValue, length, etc - return 0 for all. Everytime you get another crash add a new category. I think I had 6 or 7 when I had this issue.
The problem with NOT doing this is you have to look for the NULL everywhere in your converted objects - a PITA in my opinion.
EDIT: the code I'm using, all in a NSNull+JSON.m file:
#interface NSNull (JSON)
#end
#implementation NSNull (JSON)
- (NSUInteger)length { return 0; }
- (NSInteger)integerValue { return 0; };
- (float)floatValue { return 0; };
- (NSString *)description { return #"0(NSNull)"; }
- (NSArray *)componentsSeparatedByString:(NSString *)separator { return #[]; }
- (id)objectForKey:(id)key { return nil; }
- (BOOL)boolValue { return NO; }
#end
EDIT2: Now in Swift 3:
extension NSNull {
func length() -> Int { return 0 }
func integerValue() -> Int { return 0 }
func floatValue() -> Float { return 0 };
open override var description: String { return "0(NSNull)" }
func componentsSeparatedByString(separator: String) -> [AnyObject] { return [AnyObject]() }
func objectForKey(key: AnyObject) -> AnyObject? { return nil }
func boolValue() -> Bool { return false }
}
The error message is pretty clear. NSNull and nil are different things:
The NSNull class defines a singleton object used to represent null values in
collection objects (which don’t allow nil values).
If you want to check if authenticationToken is NSNull try: [authenticationToken isEqual: [NSNull null]]
In line with David H's answer, how about a category on NSNull that just uses ObjC's message forwarding to "do nothing", to emulate the runtime's behavior when sending messages to nil?
Like this:
#interface NSNull (ForwardInvocation)
#end
#implementation NSNull (ForwardInvocation)
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
return [NSNull methodSignatureForSelector:#selector(description)];
}
- (void)forwardInvocation:(NSInvocation *)anInvocation {
// do nothing; prevent 'unrecognized selector' crashes
}
#end
The [NSNull methodSignatureForSelector:#selector(description)]; takes advantage of the fact that NSNull inherits from NSObject, which provides the description method. This satisfies the forwarding mechanism requirement for implementing -methodSignatureForSelector:.
The problem comes because your method return an NSNull object.
You can't check [authenticationToken isEqual:[NSNull null]]) because [NSNull null] give an instance of an object. So it's different from your object itself.
If you want to check if you received an NSNull object you need to check like this: [authenticationToken isKindOfClass:[NSNull class]] instead.

Assign NSNull Object to NSString

I'm writing an iOS App where i need to get data from a SQL-Database over mobile Services from Azure.
After downloading the data I get a NSDictionary with all attributes from the SQL-Table. If an attribute is empty, the value is NSNull.
Is there a way to pass NSNull to NSString without an IF-Statement (I don't want to have 20 if statements..)?
I wrote a category just for dealing with this issue. I used it with Core Data but it should help you, too.
#interface NSDictionary (Extensions)
- (id)NSNullToNilForKey:(NSString *)key;
#end
#implementation NSDictionary (Extensions)
- (id)NSNullToNilForKey:(NSString *)key
{
id value = [self valueForKey:key];
return value != [NSNull null] ? value : nil;
}
#end
Sample use:
NSString *value = [dictionary NSNullToNilForKey:#"key"];
You can't just assign it, but you can filter out all of the NSNull instances using something like this:
NSDictionary *dictionary = // data from server
NSDictionary *filteredDictionary = [dictionary mutableCopy];
NSSet *keysToRemove = [orig keysOfEntriesPassingTest:^BOOL(id key, id obj, BOOL *stop) {
if (obj == [NSNull null]) {
return YES;
} else {
return NO;
}
}];
[filteredDictionary removeObjectsForKeys:[keysToRemove allObjects]];
Now you have the same dictionary except that every key with an NSNull has been removed.

Resources