NSNull isEqualToString Issue With JSON Response - objective-c

I am getting the following error
-[NSNull isEqualToString:]: unrecognized selector sent to instance 0x3c168090
on this line of code
cell.offerTitle.text = [voucherData objectForKey:#"offer_title"];
Could someone help me correct the problem please?
Thanks
Oliver

Is voucherData an NSDictionary?
It's possible there's an NSNull in your dictionary, and when the dictionary is trying to find the object for offer_title, it's running into trouble.
Another possibility is that [voucherData objectForKey:#"offer_title"] is returning [NSNull null], and the label is barfing when you try to pass that instead of a string.
Try setting a breakpoint in objc_exception_throw and read the stack trace – that will give you a much better idea of what's going on.
Added:
id value = [voucherData objectForKey:#"offer_title"];
if ([value isKindOfClass:[NSNull class]])
cell.offerTitle.text = #"";
else
call.offerTitle.text = value;
or
id value = [voucherData objectForKey:#"offer_title"];
cell.offerTitle.text = [value isKindOfClass:[NSNull class]] ? #"" : value;

One candidate for best practice here is to use isEqual:, not isEqualToString:. That way, if what you get is not a string, you won't get an error and the equality test will be failed in good order.
On the other hand you could argue that isEqualToString: was a good choice, because when what you got was not a string, you got an error that alerted you to the issue!
EDIT: But that's wrong; see the comments below. The isEqualToString: message is coming from UIKit, not from the OP's own code.

i make some changing for noa's answer
-(NSDictionary*)safeData:(NSDictionary*)dict{
NSMutableDictionary *dictionary = [NSMutableDictionary dictionaryWithDictionary:dict];
NSArray *keys = dictionary.allKeys;
for (int i = 0; i < keys.count; i++){
id value = [dictionary objectForKey:[keys objectAtIndex:i]];
// you can add recursive here
value = [value isKindOfClass:[NSNull class]] ? #"" : value;
[dictionary setObject:value forKey:[keys objectAtIndex:i]];
}
return dictionary; }
and use
dictionary = [self safeData:dictionary];

What that line from the console is likely telling you is that "voucherData" is not the "NSDictionary" object that you assume that it is.
Also make sure that "offerTitle" in your cell is a valid UITextField as well.

Related

removing null from arrays in Object-c

I have this snipped of code that results in an array with a whole bunch of "<null>" throughout and I need to figure out how to remove them. Obviously after smashing my head against the keyboard I'm asking for some help.
In my .h I have declared:
NSArray *sortedContacts;
NSArray *rawContacts;
And then in .m:
-(void) buildContacts {
ABAddressBook *addressBook = [ABAddressBook sharedAddressBook];
NSArray *contacts = [addressBook people];
rawContacts=contacts;
NSArray *firstNames = [rawContacts valueForKey:#"First"];
NSArray *lastNames = [rawContacts valueForKey:#"Last"];
NSArray *organization = [rawContacts valueForKey:#"Organization"];
NSMutableArray *fullNames = [NSMutableArray array];
for(int i = 0; i < [firstNames count]; i++)
{
NSString *fullName = [NSString stringWithFormat:#"%# %# %#",
[firstNames objectAtIndex:i],
[lastNames objectAtIndex:i],
[organization objectAtIndex:i]];
[fullNames addObject:fullName];
}
NSMutableArray *fullList = [[NSMutableArray alloc]initWithArray:fullNames];
[fullList removeObjectIdenticalTo: #"<null>"];
sortedContacts = [fullList sortedArrayUsingSelector:#selector(compare:)];
NSLog(#"%#",sortedContacts);
}
I've tried so many things that I just can't see the forest for the trees anymore.
The text <null> is how the singleton instance of NSNull describes itself. That is, it's what -[NSNull description] returns.
In turn, these NSNull objects are getting into your firstNames, lastNames, and organization arrays because that's what Key-Value Coding does when you call -valueForKey: on an array and some of the elements return nil when that message is forwarded on to them with the same key. That is, calling [rawContacts valueForKey:#"First"] causes NSArray to call [element valueForKey:#"First"] for each element in rawContacts and to put the result in the array it builds. But, since an array can't contain nil, if one of those elements returns nil from [element valueForKey:#"First"], an NSNull object is added in its place.
Then, you are formatting the string fullName from the corresponding elements of firstNames, lastNames, and organization. You need to check if any of those elements are NSNull using if ([value isKindOfClass:[NSNull class]]) and handling that. For instance, you might just skip that record. Or you might combine the available fields and leave out any unavailable ones.
In any case, none of the elements of fullList will be #"<null>" because formatting values into #"%# %# %#" can never result in that string. (It might be #"<null> <null> <null>" or something like that, but never just #"<null>".)
A quick look at your code suggests you cannot get any empty strings added to your array, (a) you add elements using:
[fullNames addObject:fullName];
and fullName is created using:
[NSString stringWithFormat:#"%# %# %#" ...
so even if the %#'s get replaced by nothing you'll still have 2 spaces...
Maybe this is why all the things you've tried fail, if you're looking for empty strings you won't find them.
(Addendum: Question now says you're looking for #"<null>", you won't get that either for the same reason - there is at least two spaces in your string.)
The simple answer to removing invalid entries in fullNames is not to add them in the first place. You are adding elements in a loop (for), and conditional logic (e.g. if) inside the loop to determine whether you have something valid to add - however you define "something valid" - and only add an item to fullNames if so.
HTH
I'm not really familiar with the AddressBook framework, however this might be what's causing the confusion:
The values you collect in your arrays firstNames, lastNames and organization can be of type NSString or NSNull. You have to do any null-checking within the for-loop, before the fullName-string is constructed.
Remove this useless line:
[fullList removeObjectIdenticalTo: #"<null>"];
And replace the contents of your for-loop with the following code:
for(int i = 0; i < [firstNames count]; i++)
{
NSString *firstName = [firstNames objectAtIndex:i];
NSString *lastName = [lastNames objectAtIndex:i];
NSString *org = [organization objectAtIndex:i];
NSMutableArray *namesArray = [NSMutableArray array];
if ([firstName isKindOfClass:[NSString class]])
[namesArray addObject:firstName];
if ([lastName isKindOfClass:[NSString class]])
[namesArray addObject:lastName];
if ([org isKindOfClass:[NSString class]])
[namesArray addObject:org];
if (namesArray.count > 0)
[fullNames addObject:[namesArray componentsJoinedByString:#" "]];
}

check and remove a particular string from nsmutable array without index

In Xcode, I store some NSString in NSMutableArray.
Hello
Here
MyBook
Bible
Array
Name2
There
IamCriminal
User can enter string.
Name2
I need to delete that particular string from NSMutableArray without knowing index of the string. I have some idea that, Use iteration. Any other Best way. Plz Give with sample codings.
you can use containsObject method in an NSMutableArray
if ([yourArray containsObject:#"object"]) {
[yourArray removeObject:#"object"];
}
Swift:
if yourArray.contains("object") {
yourArray = yourArray.filter{ $0 != "object" }
}
[array removeObject:#"Name2"];
The documentation for NSMutableArray’s removeObject: method states:
matches are determined on the basis of an object’s response to the
isEqual: message
In other words, this method iterates over your array comparing the objects to #"Name2". If an object is equal to #"Name2", it is removed from the array.
[array removeObject:#"name2"];
You can try this
for(NSString *string in array)
{
if([[string lowercasestring] isEqualToSring:[yourString lowercasestring]])
{
[array removeObject:string];
break;
}
}
YOu can simply use
[yourArray removeObject:stringObjectToDelete];
This method uses indexOfObject: to locate matches and then removes them by using removeObjectAtIndex:. Thus, matches are determined on the basis of an object’s response to the isEqual: message. If the array does not contain anObject, the method has no effect.
Try this,
BOOL flag = [arrayName containsObject:Str];
if (flag == YES)
{
[arrayName removeObject:Str];
}

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:#""].

How to efficiently access large objects in Obj-C using objectForKey and objectAtIndex?

If I have a large NSDirectory typically from a parsed JSON-object I can access this object with code like so:
[[[[[obj objectForKey:#"root"] objectForKey:#"key1"] objectAtIndex:idx] objectForKey:#"key2"] objectAtIndex:idz];
The line might be a lot longer than this.
Can I optimize this code in any way? At least make it easier to read?
This line will also generate a runtime-error if the object does not correspond, what is the most efficient way to avoid that?
If you were using -objectForKey: for everything you could use -valueForKeyPath:, as in
[obj valueForKeyPath:#"key1.key2.key3.key4"]
However, this doesn't work when you need to use -objectAtIndex:. I don't think there's any good solution for you. -valueForKeyPath: also wouldn't solve the problem of the runtime errors.
If you truly want a simple way to do this you could write your own version of -valueForKeyPath: (call it something else) that provides a syntax for specifying an -objectAtIndex: instead of a key, and that does the appropriate dynamic checks to ensure the object actually responds to the method in question.
If you want easier to read code you can split the line into several lines like this
MyClass *rootObject = [obj objectForKey:#"root"];
MyClass *key1Object = [rootObject objectForKey:#"key1"];
MyClass *myObject = [key1Object objectAtIndex:idx];
...
and so forth.
I think, you can create some array, that will contain full "path" to your object. The only thing, you need to store your indexes somehow, maybe in NSNumber, in this case you cannot use NSNumber objects as keys in your dictionaries. Then create a method, that will return needed object for this given "path". smth like
NSMutableArray* basePath = [NSMutableArray arrayWithObjects: #"first", [NSNumber numberWithInt:index], nil];
id object = [self objectForPath:basePath inContainer:container];
- (id) objectForPath:(NSMutableArray*)basePath inContainer:(id)container
{
id result = nil;
id pathComponent = [basePath objectAtIndex: 0];
[basePath removeObjectAtIndex: 0];
// check if it is a number with int index
if( [pathComponent isKindOfClass:[NSNumber class]] )
{
result = [container objectAtIndex: [pathComponent intValue]];
}
else
{
result = [container objectForKey: pathComponent];
}
assert( result != nil );
// check if it is need to continue searching object
if( [basePath count] > 0 )
{
return [self objectForPath:basePath inContainer: result];
}
else
{
return result;
}
}
this is just an idea, but I hope you understand what I mean. And as Kevin mentioned above, if you don't have indexes, you can use key-value coding.
Don't know if it can suit you, but you could also give a try to blocks, I always find them very convenient. At least they made code much more readable.
NSArray *filter = [NSArray arrayWithObjects:#"pathToFind", #"pathToFind2",nil];
NSPredicate *filterBlock = [NSPredicate predicateWithBlock: ^BOOL(id obj, NSDictionary *bind){
NSArray *root = (NSArray*)obj;
// cycle the array and found what you need.
// eventually implementing some sort of exit strategy
}];
[rootObject filteredArrayUsingPredicate:filterBlock];

What would cause objectForKey: to return null with a valid string in place?

I am having an issue with NSDictionary returning null for an NSString even though the string is in the dictionary. Here is the code:
- (void)sourceDidChange:(NSNotification *)aNote {
NSDictionary *aDict = [aNote userInfo];
DLog(#"%#", aDict);
NSString *newSourceString = [aDict objectForKey:#"newSource"];
DLog(#"%#", newSourceString);
newSourceString = [newSourceString stringByReplacingOccurrencesOfString:#" " withString:#""];
DLog(#"%#", newSourceString);
NSString *inspectorString = [newSourceString stringByAppendingString:#"InspectorController"];
DLog(#"%#", inspectorString);
newSourceString = [newSourceString stringByAppendingString:#"ViewController"];
DLog(#"%#", newSourceString);
}
And I get the following log statements:
2010-04-17 23:50:13.913 CoreDataUISandbox[13417:a0f] -[RightViewController sourceDidChange:] { newSource = "Second View"; }
2010-04-17 23:50:13.914 CoreDataUISandbox[13417:a0f] -[RightViewController sourceDidChange:] (null)
2010-04-17 23:50:13.916 CoreDataUISandbox[13417:a0f] -[RightViewController sourceDidChange:] (null)
2010-04-17 23:50:13.917 CoreDataUISandbox[13417:a0f] -[RightViewController sourceDidChange:] (null)
2010-04-17 23:50:13.917 CoreDataUISandbox[13417:a0f] -[RightViewController sourceDidChange:] (null)
As you can see, the string is in the dictionary under the key newSource, yet when I call objectForKey:, I get null. I have even tried the fallback option of cleaning the project.
Has anyone ever run into this, or have I just forgotten something really basic?
At this point, you're left with a reporting error from DLog for some reason.
Try:
Logging with NSLog.
Check the value of newSourceString directly in the debugger while the code is live.
What would cause objectForKey: to return null with a valid string in place?
One of two things:
The dictionary does not contain an object for that key. (Whether you think it does is irrelevant.)
You don't have a dictionary; aDict is nil, so you're sending the objectForKey: message to nil. Messages to nil do nothing but return nil.
As you can see the string is in the dictionary under the key newSource…
Actually, I'm not sure what's going on there. An NSDictionary's description (if it contained a string for that key) would be { newSource = "some string here"; }, which doesn't match the description you logged. On the other hand, if it were an object that isn't a dictionary, you should get a “does not respond to selector” exception upon trying to send it an objectForKey: message. So while it does appear, from your log output, to be something, I have no idea what it is, except that it is probably not a dictionary.
That's just plain strange, then.
I ran into a similar issue. For me the problem was that I thought my key was a NSString when it was actually a NSNumber. You can check your key using the following
for (id key in [aDict allKeys]){
NSLog(#"%#:%#",key, [[key class] description]);
}
}
You fail to neglect the environment in which you're coding. If it's with GNUstep, specifically, gnustep1.19, read on. Otherwise ignore.
I just encountered a very odd bug with gnustep1.19(.3) but it mimics this question perfectly.
NSString * key = <some string>
NSDictionary * dict = <some dictionary>
(gdb) p [dict objectForKey:key]
$20 = (struct objc_object *) 0x0
(gdb) p [dict objectForKey:#"MyKeyValue"]
$22 = (struct objc_object *) 0x7fffd94fe690
(gdb) p [key compare"#MyKeyValue"]
$25 = NSOrderedSame
In this case, 'key' was being initialised by extracting it from another NSDictionary, and some of the entries in the other dictionary (loaded from a file) contain Unicode characters. That is, so far, the only correlation I have found - removing the unicode from the source file and re-running the app makes it work.
This is not an issue for gnustep1.18 or >=gnustep1.20
I suspect that your string is actually, literally "(null)" -- that is to say, it is 6-letters long, and spells out (-n-u-l-l-).
I suspect that aDict is not an instance of NSDictionary.
log it's class to confirm.