Why is this code not recognising the NSString as being equal? - ios

This is the code I have:
NSLog(#"name: %#", name);
NSLog(#"service: %#", service.name);
if (name == service.name) {
NSLog(#"Test");
}
Name is "Andrew’s MacBook Pro".
Service is "Andrew’s MacBook Pro"
And yet I don't get a "Test" from NSLog. Any ideas why this could be?

use [string isEqualToString:#"any string"]
See a very useful discussion here: Understanding NSString comparison

For string comparisons, use [name isEqualToString:service.name]
Using == will compare to see if both pointers point to the same object, not if they point to objects with the same contents. Even if both pointers contain the same string, that does not mean they point to the same object.
If two people both have the same car, and so have the same key to unlock it, both keys are not equal and will not open both cars; each will only open the car for which it was made. If one person has a car but has an extra key made, they are equal because they open the same car (object). You can think of the pointers in this way.

You are comparing two objects not two string. Try [string isEqualToString:#"another string"].

In Objective C use [string1 isEqualToString:#"string2"]; for string comparison.
Here is the code :
NSLog(#"name: %#", name);
NSLog(#"service: %#", service.name);
if ([name isEqualToString:service.name])
NSLog(#"Strings are Equal");
else
NSLog(#"Strings are Not Equal");

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.

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.

NSString: Best way to check if string contains another string with special format?

I have a string representing fruits, separated by dot:
apple.orange.banana.watermelon
and each fruit may have a tag:
apple.orange.[old]banana.[juicy]watermelon
also there may be fruit names which are partially overlapping:
apple.orange.[old]banana.[juicy]strawberry.[fresh]berry
also the tags may be same as fruit names:
apple.orange.[old]banana.[berry]strawberry.[fresh]berry
I need to check if such a string contains a specified fruit, so given the above string and a fruit name, say "berry", I want to know the string contains "berry" and of course it should not tell me YES if "berry"'s not there but "strawberry" is.
A quick way came up in my mind is:
use componentsSeparatedByString to get an array of components (fruit names with tags)
go through each component, check if it has a tag (ie square brackets), remove it if YES
then check the remaining string is exactly the given fruit name
I cannot just use the whole string with rangeOfString because "berry" is a substring of "strawberry", I can't even first get components then check substring for each component because tags may be the same as fruit names.
I wonder is there any better way to do this? Better in terms of memory footprint and/or speed?
Thanks!
There are plenty of ways to do this.
You could use a regular expression.
You could split the string into parts.
But instead, let's notice that, when “berry” (the desired fruit) isn't the first or last fruit in the string, it's got to appear as either ]berry. or as .berry., and neither of those can match if “berry” is a substring.
So we can solve this problem quite simply putting a . on each end of the string before searching it for one of those patterns:
BOOL stringContainsFruit(NSString *string, NSString *fruit) {
string = [NSString stringWithFormat:#".%#.", string];
NSString *dotFruit = [NSString stringWithFormat:#".%#.", fruit];
if ([string rangeOfString:dotFruit].location != NSNotFound) {
return YES;
}
NSString *bracketFruit = [NSString stringWithFormat:#"]%#.", fruit];
if ([string rangeOfString:bracketFruit].location != NSNotFound) {
return YES;
}
return NO;
}

Check if Text Field NEARLY Matches Set Text? iOS

Does anyone now how I can check if a text field NEARLY matches a set text?
I know how to check if it exactly matches, but i want it to know if its even close to the set text
So if they type HELLO WORD it indicates its close but not exact match?
if (([textfield.text isEqual:#"HELLO WORLD"]))
{
NSLog(#"Correct");
} else {
NSLog(#"Incorrect");
}
This library may be of use to you. And since it's open source, you can check the source to see how it's done. :)
Use this
For Case Insensitive :
if( [textfield.text caseInsensitiveCompare:#"My Case sensitiVE"] == NSOrderedSame ) {
// strings are equal except for possibly case
}
For Case Sensitive :
if([textfield.text isEqualToString:#"My Case sensitiVE"]) {
// Case sensitive Compare
}
You can compare each index of two string and see how many difference is there. And you should define your "nearly match", it may be difference in single character or in multiple character. And decide if you should accept it or reject it.
If you like algorithm Longest Common Subsequence is a key to your goal.. :)
use
NSString caseInsensitiveCompare:
or
- (NSComparisonResult)compare:(NSString *)aString
options:(NSStringCompareOptions)mask`
NSString *string = #"HELLO WORLD I AM JACK";
if ([string rangeOfString:#"HELLO WORLD"].location == NSNotFound) {
NSLog(#"string does not contain HELLO WORLD");
} else {
NSLog(#"string contains HELLO WORLD!");
}

Integer from .plist

I'm new to plists, and I really need to use one. What I have is a plist where different numbers are stored, under two dictionaries. I need to get that number which is stored, and make it an integer. This process will be run from a method called 'readPlist.
The plist is called 'properties.plist'. The first dictionary is called 'Enemies'. It contains various other dictionaries, which will have the name stored in the NSMutableString called 'SpriteType'. The name of the number will have the format 'L - %d', with the %d being an integer called 'LevelNumber'.
If possible, can someone give me the code on how to get that integer using the information, and the names of dictionaries above.
I have looked around at how to access plists, but the code that people have shown doesn't work.
Thanks in advance.
EDIT: Too make it more understandable, this is my plist. What i want in an integer, called 'SpriteNumber' to be equal to the value of 'L - %d'
If you read the contents of your plist into a dictionary (I won't tell you how to do it, but this is the tutorial I refer to often), then it's a matter of getting the string out of the key for the level with [[myDictionary objectForKey:#"key"]stringValue];. Then, using of NSString's extremely helpful -stringByReplacingOccurencesOfString:withString: to get rid of the "L -" part and only get a numerical value. Finally, get an integer from the string with [myString intValue].
well, the easiest way would be something like :
-(int) getMosquitoCountForLevel:(int) level {
int mosquitoCount=0;
NSString *gsFile = #"whateverFullyQualifiedFileNameYourPlistIs";
NSDictionary* definitions = [NSDictionary dictionaryWithContentsOfFile:gsFile];
NSDictionary* mosquitos = [definitions objectForKey:#"Mosquito"];
if(mosquitos) {
NSString *levelKey = [NSString stringWithFormat:#"L - %d",level];
NSNumber *mosquitoCountAsNumber = [mosquitos objectForKey:levelKey];
if(mosquitoCountAsNumber) {
mosquitoCount=[mosquitoCountAsNumber intValue];
} else {
CCLOGERROR(#"%# - Mosquito definitions in %# does not contain a en entry for level %#.",self.class,gsFile,levelKey);
}
} else {
CCLOGERROR(#"%# - file %# does not contain a Mosquito dictionary.",self.class,gsFile);
}
return mosquitoCount;
}
this compiles but not tested with actual data.

Resources