Best way to check for variable's class in objective C? - iphone

I have an object which I am not sure is an NSString or not (could be NSNull, for example when reading a json into an NSDictionary) and I would like to get an NSString* if it is a valid string, nil otherwise.
Is there an accepted way of doing this except writing my own function?
+(NSString*)stringWithMaybeString:(id)maybeString {
if ( [maybeString isKindOfClass:[NSString class]] )
return maybeString;
return nil;
}

Your method looks like the accepted way of doing this to me :)
Possibly you could add it to NSString using a category.

Related

Find Existence of Key in NSDictionary

How can we identify the object is available for a particular key. I have tried following:
for(NSDictionary *item in jsonArray){
if([item objectForKey:#"EventDate"])
NSLog([item objectForKey:#"EventDate"]);
}
This is getting crash the code with error:
-[__NSCFString objectForKey:]: unrecognized selector sent to instance 0x6a567b0
I have also find many posts that is showing objectForKey will return nil if a key doesn't exists. Than my question is there is also a method in NSDictionary class that is "setNilValueForKey". How is this possible that we cannot specify the NSDictionary key with nil object and also we have the method to set nil value for object in NSDictionary.
Please Suggest on first and also make me clear on second query.
1) Your jsonArray contains other types of objects than NSDictionaries, including at least one NSString. NSString doesn't respond to objectForKey: so it throws an exception when you try to call it. You'll have to look at the JSON to determine how to proceed with whatever you were doing.
2) There is an NSObject method setNilValueForKey: which is related to key-value coding. This isn't really related to NSDictionary. If you really need to represent nil in your dictionary, set [NSNull null] as the object for your key that represents nil.
Hope this helps!
Not all the objects in your array are dictionaries. You need to check what kind of data you're processing before you work on it. Something like this:
for(NSObject* item in jsonArray) {
if ([item isKindOfClass:[NSDictionary class]]) {
// do dictionary stuff
}
else if ([item isKindOfClass:[NSString class]]) {
// do string stuff
}
}
item is not a NSDictionary its a String. So check your jsonArray it may be contains only strings not dictinaries
Answer to second query
There is a non-nil object called NSNull that is built specifically to represent nils in situations where "plain" nil is not acceptable. If you replace your nils with [NSNull null] object, NSDictionary will take them. You would need to check for NSNull on the way out, though.
Refer more here
Answer to first query
for(id *item in jsonArray)
{
if([item isKindofClass:[NSDictionary class])
{
NSArray *allKeys = [item allKeys];
for(NSString *strKey in allKeys)
{
if([strKey isEqualToString:#"EventDate"])
{
// EventDate key has object
NSLog([item objectForKey:#"EventDate"]);
}
}
}
}

What is the difference between NSString = #"HELLO" and NSString getting that same string using initWithContentsOfURL

I have a string that is getting the content from URL and when I try to use it, it doesn't work the way I thought it should.
When I initialize NSString with with contents of URL like this:
NSString *strFromURL = [[NSString alloc] initWithContentsOfURL:someURLReturningTextHELLO encoding:NSUTF8StringEncoding error:nil];
NSLog(#"%#",strFromURL); // returns "HELLO" // as expected
but when I try:
if (strFromURL == #"HELLO") { NSLog(#"IT WORKS"); } // This doesn't happen
When I do the same process with:
NSString *mySimpleString = #"HELLO";
if (mySimpleString == #"HELLO") { NSLog(#"IT WORKS"); } // This works
So, my question is, how can I get content from URL that I can use later in my IF statement?
*I'm new to Objective-C. Thanks!
When you asking for if (strFromURL == #"HELLO") you're comparing equality of objects, but not strings. When you call comparison of two constant strings it works, other it fails whether strings in compared objects are equal or not.
Call if ([strFromURL isEqualToString:#"HELLO"]) instead.
With objects, the == operator tests for pointer equality. That is, the two variables are the same if the pointers both point to the same object. The string fetched from the URL is not the same object as the constant string, so it fails. You want to use NSString's isEqualToString: method, which tests for whether the strings themselves are equal rather than the pointers.
You can try to compare with a isEqualToString method

[NSCFString stringValue]: unrecognized selector sent to instance

I'm using this code to query core data and return the value of key, I store the value like this :
NSString *newName= #"test";
[newShot setValue:newName forKey:#"shotNumber"];
and I query like this :
NSManagedObject *mo = [items objectAtIndex:0]; // assuming that array is not empty
NSString *value = [[mo valueForKey:#"shotNumber"] stringValue];
NSLog(#"Value : %#",value);
I'm crashing with this message though :
[NSCFString stringValue]: unrecognized selector sent to instance,
does anyone know where that would be coming from ?
newName (#"test") is already an NSString. There is no need to call -stringValue to convert it to a string.
NSString *value = [mo valueForKey:#"shotNumber"];
I often times add a category for NSString to handle this:
#interface NSString(JB)
-(NSString *) stringValue;
#end
#implementation NSString(JB)
-(NSString *) stringValue {
return self;
}
#end
You can add a similar category to other classes that you want to respond this way.
[mo valueForKey: #"shotNumber"] is returning a string and NSString (of which NSCFString is an implementation detail) do not implement a stringValue method.
Given that NSNumber does implement stringValue, I'd bet you put an NSString into mo when you thought you were putting in an NSNumber.
The value for the key #"shotNumber" is probably of type NSString which is just a wrapper for NSCFString. What you need to do, is, instead of stringValue, use the description method.
Note that you could also get this problem if you are trying to access a string property on an object that you think is something else, but is actually a string.
In my case I thought my Hydration object was in fact a hydration, but checking its class via isKindOfClass I found that it was an NSString and realized that I had incorrectly cast it as a Hydration object and that my problem lied further up the chain.

Crash-proof NSDictionary

I'm looking to create a "crash-proof" NSDictionary as I'm using a JSON serializer that converts a server response into an NSDictionary. As as result, sometimes the key for the dictionary is not present. Currently, this will crash the application, however I'd rather an empty NSString was returned so I can display this in the interface.
A solution could be that I check for the key every time I access the dictionary, e.g.
if([returnedDictionary objectForKey:#"key"]){
// Display [returnedDictionary objectForKey:#"key"];
}else{
// Display #"";
}
However this soon results in bloated, hard-to-read code.
I had thought about creating a custom NSDictionary object, something like:
#interface NSSafeDictionary : NSDictionary .....
that overrides objectForKey with the above statement.
Is this a satisfactory approach?
Thanks
Are you always going to want to get strings out of your dictionary or will other objects be stored in it as well? If it's only strings, I think the easiest way around this is to construct a category on NSDictionary.
#interface NSDictionary ( EmptyStrings )
- (NSString *)stringForKey:(id)aKey;
#end
#implementation NSDictionary ( EmptyStrings )
- (NSString *)stringForKey:(id)aKey {
id object = [self objectForKey:aKey];
if (object == nil ) {
return #"";
}
if ([object isKindOfClass:[NSString class]) {
return object;
} else {
return nil;
}
}
#end
Given that it comes in over the network, I would think that you would want to sanitise the data more than just checking for empty values but if not, you don't really need to inherit from NSDictionary.
A simple utility method in your class would do the trick.
Or you could create a category on NSDictionary:
#interface NSDictionary (Safe)
-(NSString*)safeStringForKey:(NSString*)key;
#end
(I'm sure you can figure out the implementation.)

Initial value of a Core Data Entities properties?

I have several core data entities that contains a bunch of empty NSString properties.
I parse some XML and set the properties I can get a hold of and would like to set the "empty" ones to "n/a" as in "not available". So if my XML does not contain the values it sort of tidy up the Entity by giving it a "n/a" string I can test for later on, also should one of these make it to a UILabel it will not display (null) .. which leads me to my question:
I do this to test if the property on the Entity is already sat, is nil or is empty:
for(NSString *s in allPossibleStrings) {
if([[f valueForKey:s] isKindOfClass:[NSString class]] && [[f valueForKey:s] isEqualToString:#""]) {
[f setValue:#"n/a" forKey:s];
}
if ([[f valueForKey:s] isKindOfClass:[NSString class]] && [f valueForKey:s] == nil) {
[f setValue:#"n/a" forKey:s];
}
}
It turns out however that I still end up with a lot of values being displayed as (null).
So I was thinking can the property be something else than #"" empty or (nil)
I believe the NSManagedObject should be KVC compliant so I did a test where I copied my NSManagedObject property by property, only difference is that is is a subclass of just NSObject instead of NSManagedObject.
Sadly this behaves in the exact same way. It also leaves values as (null)
Hope someone can pick up on where I go wrong with these string tests :)
Thank You
You could set the default property value of your entity to "N/A" (that's a good practice, because you might want to use sqlite for iPhone app shipping, and it doesn't work well with null values because sqlite and cocoa don't have the same vision of "null") and set "optional" to "No".
if([[f valueForKey:s] isKindOfClass:[NSString class]] && [[f valueForKey:s] isEqualToString:#"NA"]) {
//Tell your program to know that it's null if it needs to know
}
Cheers
What is allPossibleStrings? You could do this to get to all the properties:
NSEntityDescription *entity = [NSEntityDescription entityForName:#"MyEntityName" inManagedObjectContext:moc];
for (NSString *attr in [entity attributesByName]) {
[object setValue:#"n/a" forKey:attr];
}
The issue is that when something is nil, what you can get back from any managed object property (not just strings) is an NSNull object. So if you check also for isKindOfClass:[NSNull class] I think your code will do what you want.