iOS - exception when checking if string is empty - objective-c

I am doing something like this:
// GET THE USER ID
NSUserDefaults *standardUserDefaults = [NSUserDefaults standardUserDefaults];
NSString *user_id = [standardUserDefaults objectForKey:#"user_id"];
And then checking whether the user_id is empty
if ([user_id length] == 0) {
proceed = false;
NSLog(#"Error: User id is not set.");
}
And I get this runtime exception:
* Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSCFBoolean length]: unrecognized selector sent to instance 0x1469f70'
Any idea why I get the exception? I didn't think there was anything too wrong with what I was doing.
Thanks!

This:
NSString *user_id = [standardUserDefaults objectForKey:#"user_id"];
Is returning an NSNumber (as NSCFBoolean is a private subclass of NSNumber) rather than a string. It therefore doesn't implement length, causing the exception.
Perhaps you want to test [user_id intValue] > 0? Even if you convert it to a string it'll always have some length.
(side issues raised: merely declaring user_id as a reference to an NSString doesn't mean that anything you assign to it magically becomes a string; indeed there are no type object-type coercion effects whatsoever. The compiler doesn't complain because the NSUserDefaults return objects of type id, i.e. it guarantees they're objects but makes no claims as to their type, and the compiler doesn't know either. All objects may be cast to and from id without generating a warning, so that it can be used by classes such as the user defaults, NSArray, etc, where they can accept anything as long as it's an object).
EDIT: based on issues raised in the comments, it sounds like the thing originally being stored may not be a string. A good way to validate web stuff is probably something like:
// fall through here if the object isn't a string
if(![responseString isKindOfClass:[NSString class]])
{
// check whether it's something that can be converted to a string
if(![responseString respondsToSelector:#selector(stringValue)])
{
// some sort of error condition; the server returned something
// that isn't a string and doesn't convert (in and of itself) to string
}
else
{
// convert the object to a string
responseString = [responseString stringValue];
}
}

The reason you are getting that error is you are trying to call 'length' on what appears to be a boolean. Either way, for checking if a string is blank here are some easy methods you can add to the NSString class by means of a category:
-(BOOL)isBlank{
return [[self trim] length]==0;
}
-(NSString *)trim{
return [self stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet] ];
}
Then to call it it's just:
[myString isBlank];

Related

Invalid Selector on doubleValue

A bit new to Objective-C, so please bear with me.
Firstly, I'm using the FMDB library for SQLite management.
I'm populating an NSMutableDictionary using the following method:
//....
while([effectivenessResults next]) //this loops through the results of a query (verified that this works)
{
NSMutableArray *dFactors = [[NSMutableArray alloc]init];
if([resultDict objectForKey:[effectivenessResults stringForColumn:#"tName"]])
{
dFactors = [resultDict objectForKey:[effectivenessResults stringForColumn:#"tName"]];
}
NSNumber *effectivenessValToAdd = [NSNumber numberWithDouble:[effectivenessResults doubleForColumn:#"dFactor"]/100];
[dFactors addObject:[NSMutableString stringWithFormat:#"%#",effectivenessValToAdd]];
[resultDict setObject:dFactors forKey:[effectivenessResults stringForColumn:#"tName"]];
}
I'm returning the array properly (I have verified this). Then, I am accessing this NSMutableDictionary elsewhere, using the follwing method:
for(id type in tEffect) //tEffect is the NSMutableDictionary, returned from the previous code (there known as resultDict)
{
effectivenessString = [self getEffectivenessString:[tEffect objectForKey:type]];
tInfo = [NSMutableString stringWithFormat:#"%#", [tInfo stringByAppendingFormat:#"%#: %#\n", type, effectivenessString]];
}
which calls the following two methods:
-(NSMutableString *)getEffectivenessString:(NSNumber *) numberPassedIn
{
double dFactor = [numberPassedIn doubleValue];
//adds the above value to a string, this will not affect anything
}
and
-(NSNumber *) listProduct: (NSMutableArray *)listOfValues //calculates the product of an NSMutableArray of numbers
{
NSNumber *product=[NSNumber numberWithDouble:1.0];
for(int i = 0; i < [listOfValues count]; i++)
{
NSNumber *newVal = [listOfValues objectAtIndex:i];
product = [NSNumber numberWithDouble:[product doubleValue] * [newVal doubleValue]];
}
return product;
}
So, when I call these methods, I am getting the following error:
2013-08-04 13:52:04.514 effectCalculator[45573:c07] -[__NSArrayM doubleValue]:
unrecognized selector sent to instance 0x8c19e00
2013-08-04 13:52:04.521 effectCalculator[45573:c07] *** Terminating app due to uncaught
exception 'NSInvalidArgumentException', reason: '-[__NSArrayM doubleValue]: unrecognized
selector sent to instance 0x8c19e00'
Important to note: This error occurs on the retrieval, NOT the populating of the NSMutableDictionary. This means the population of this dictionary not the an issue, but it might have something to do with why it's having trouble retrieving the data.
So what might cause this error?
Your code is pretty difficult to follow. In future please post a minimal sample which compiles, or at least is a single block of understandable code.
Having said that, I believe your issue is with this bit:
for(id type in tEffect) //tEffect is the NSMutableDictionary, returned from the previous code (there known as resultDict)
{
effectivenessString = [self getEffectivenessString:[tEffect objectForKey:type]];
what does resultDict contain?
[resultDict setObject:dFactors ...
but dFactors is an NSMutableArray. Well getEffectivenessString expects a NSNumber, not a NSMutableArray. So it complains. Also I think you intended for the method to take a string, not a number, although I don't see why you're not casting as you load them (instead of as you use them).
Since Objective C doesn't support strongly-typed arrays or dictionaries, your best bet to defend against this in the future is to name your variables more logically. It should stand out when you try to call a method which expects a number with an array instead.

-[NSNull objectForKeyedSubscript:]: unrecognized selector sent to instance

I got an exception that says:
-[NSNull objectForKeyedSubscript:]: unrecognized selector sent to instance
Is it saying I am trying to access an NSNull object with a key?
Any idea what causes this and how to fix it or debug further?
The way to fix it is to not attempt objectForKeyedSubscript on an NSNull object. (I'm betting you're handling some JSON data and aren't prepared for a NULL value.)
(And apparently objectForKeyedSubscript is what the new array[x] notation translates into.)
(Note that you can test for NSNull by simply comparing with == to [NSNull null], since there's one and only one NSNull object in the app.)
What ever value you are storing, despite what the editor tells you, at run time you are storing an NSNull, and later on trying to call objectForKeyedSubscript. I am guessing this happening on what is expected to be an NSDictionary. Some thing like:
NSString *str = dict[#"SomeKey"]
Either a piece of code beforehand is not doing its job and investigate there, or perform some validation:
NSDictionary *dict = ...;
if ( [dict isKindOfClass:[NSDictionary class]] ) {
// handle the dictionary
}
else {
// some kind of error, handle appropriately
}
I often have this kind of scenario when dealing with error messages from networking operations.
I suggest adding a category to NSNull to handle this in the same way you would expect a subscript call to be handled if it it were sent to nil.
#implementation NSNull (Additions)
- (NSObject*)objectForKeyedSubscript:(id<NSCopying>)key {
return nil;
}
- (NSObject*)objectAtIndexedSubscript:(NSUInteger)idx {
return nil;
}
#end
A simple way to test is like this:
id n = [NSNull null];
n[#""];
n[0];
With this category, this test should be handled successfully/softly.

if statement for dictionary objectforkey:#"key" when value is <null>

I am working with objective c for an iphone app.
I see that [dictionary objectForKey:#"key"] return <null>. Doing a if([dictionary objectForKey:#"key"] == nil || [dictionary objectForKey:#"key"] == null) does not seem to catch this case.
Doing a if([[dictionary objectForKey:#"key"] isEqualToString:#"<null>"]) causes my program to crash.
What is the correct expression to catch <null>?
More Details
An if statement for nil still isn't catching the case... Maybe i'm just too tired to see something, but here's additional info:
Dictionary is populated via a url that contains json data like so:
NSURL *url = [NSURL URLWithString:"http://site.com/"];
dataresult = [NSData dataWithContentsOfURL:url];
NSError *error;
NSMutableDictionary *dictionary = [NSJSONSerialization JSONObjectWithData:dataresult options:kNilOptions error:& error];
doing an NSLog on the dictionary gives this output:
{
key = "<null>";
responseMessage = "The email / registration code combination is incorrect";
}
You have an instance of NSNull. Actually, the instance, since it's a singleton.
Cocoa collections can't contain nil, although they may return nil if you try to access something which isn't present.
However, sometimes it's valuable for a program to store a thing meaning "nothing" in a collection. That's what NSNull is for.
As it happens, JSON can represent null objects. So, when converting JSON to a Cocoa collection, those null objects get translated into the NSNull object.
When Cocoa formats a string with a "%#" specifier, a nil value will get formatted as "(null)" with parentheses. An NSNull value will get formatted as "<null>" with angle brackets.
New answer:
Thanks for adding the detail. It looks like the "dataresult" you are setting is not a JSON object so no wonder you're getting wacky results from putting a raw string into "[NSJSONSerialization JSONObjectWithData:]. You may need to do some basic error checking on your data before you call anything JSON related.
Original answer:
First off, if this were my code, I wouldn't name a "NSDictionary" object "array" (and I see you caught my comment in your edit... hope you get some sleep soon!).
Secondly, what you are looking for is "nil", not a string named "<null>". As Apple's documentation for objectForKey: states, if an object is not found for the key you are asking for, a nil is returned. The Xcode console tries to be helpful in converting nil objects to "<null>" strings in it's output.
Do "if [dictionary objectForKey: #"key"] != nil" and you should be happier.
Just use the following code:
if ([[dictionary valueForKey:#"key"] isKindOfClass:[NSNull Class]]{
//This means that the value inside the dictionary is <null>
}
else{
//the value is not <null>
}
This should do it.

how to handle an data from server without knowing the object type (aka, could be a dictionary or array)

so i make a lot of server calls for my app. what is returned can depend on the result of the server operation.
say i make an api call to "foo" which will return either a hash map/nsdictionary if successful or a bool (or a 0 for false, meaning it did not execute).
with my code, i typecast it to i believe it should be assuming it was a successful operation. i will check to see if i get back something else then i expected, say a BOOL false.
NSString *mapContext = (NSString *) [call xmlrpcCall:#"load_map" withObjects: [NSArray arrayWithObjects:dataCenter.state,nil]];
NSLog(#"mapContext in loadStateMap: %#", mapContext);
if ([mapContext isKindOfClass:[NSDictionary class]])
{
if ([mapContext objectForKey:#"faultCode"])
{
NSLog(#"mapContext: %#", mapContext);
[self defaultAlert:mapContext titleMsg:#"load_map"];
}
}
here i ask the server to load a map. if successfull, it will return a string. if it fails, it will return a dictionary with a fault code and a fault message. since mapContext is instantiated as a string, when i check to see if its a dictionary and check for a key fault code, xcode gives me a warning that mapContext may not respond to "objectForKey". i understand completely why i get the warning, but is there a way i can prevent the warning? it never breaks the app but its annoying to see 30+ warnings about this issue.
Use id, this is what it is for and that is why so many abstracted foundation classes use them (NSArray anyone).
//problem solved!
id mapContext = [call xmlrpcCall:#"load_map" withObjects: [NSArray arrayWithObjects:dataCenter.state,nil]];

Is using respondsToSelector: good style?

Is it "better style" to send a message and hope the object responds, or to check to see if it responds to a selector and have some sort of fallback if it doesn't.
For example:
- (NSString *)stringForObjectValue:(id)obj {
if ([obj respondsToSelector:#selector(intValue)]) {
NSString *roman = [self formatRomanNumber:[obj intValue] resultSoFar:#""];
return roman;
} else {
return [NSString stringWithFormat:#"can't format a %#", [obj class]];
}
}
vs.
- (NSString *)stringForObjectValue:(id)obj {
NSString *roman = format_roman(#"", [obj intValue]);
return roman;
}
(the example is from a NSNumberFormatter subclass...but it could be from a NSObjectFormatter subclass...)
If you're not 100% sure that all instances that come to your (stringForObjectValue) function respond to selector then you must perform that check to avoid crashes in runtime.
How to handle the cases when obj does not respond to intValue selector may depend on particular context where your method is used. For example you may return nil object from method in that case so you can easily see that something went wrong
If you don't know the exact type then using respondsToSelector: is definitely good style, because you risk an exception otherwise. And it's so important that there's a name for this technique: Duck Typing.