This may sound like a silly question but Apple provides us with NSNotFound but why didn't they provide one called NSFound? Is there a way one can define a NSFound macro on their own?
The reason I am asking all this is that in order for me to check if a string "contains" a certain character I have to do double negative i.e.
if ([XML rangeOfString:#"error" options:NSCaseInsensitiveSearch].location != NSNotFound)
{
//server is down
}
else
{
//server is up
}
At least for me this would have been so much easier to read if I could simply do this instead
if ([XML rangeOfString:#"error" options:NSCaseInsensitiveSearch].location == NSFound)
{
//server is down
}
else
{
//server is up
}
If I want to define NSFound or SAMFound, how would I go about doing that?
Your issue is really with the design pattern methods like rangeOfString follow - using a single return value for both valid results, of which there are many, and failure indications, of which there is one. You can test for a single failure value with a comparison to a constant, NSNotFound in this case, but you cannot likewise test for many possible values with a simple comparison - instead you use the "double negative" you don't like.
If you find it too ugly change it... Maybe:
#interface NSString (SamExtras)
- (BOOL) SAMcontainsString:(NSString *)string options:(NSStringCompareOptions)options;
#end
#implementation NSString (SamExtras)
- (BOOL) SAMcontainsString:(NSString *)string options:(NSStringCompareOptions)options
{
return [self rangeOfString:string options:options].location != NSNotFound;
}
#end
Which would allow you to use:
if ([XML SAMcontainsString:#"error" options:NSCaseInsensitiveSearch])
{
//server is down
}
else
{
//server is up
}
with no double negative. You can write the category once and use it in all your projects.
HTH
Double Negative doesn't have the consequences in code as it does in grammar.
The reason they provide a not found, as opposed to a found version, is simply the not found value is a single (supposedly invalid) value and everything else is valid. It's therefore simpler to define this single, invalid value.
Also it makes more sense (more efficient, avoiding a double-search and less code) to store the NSRange in a local variable in order to firstly test for validity and then to use the value:
NSRange range = [XML rangeOfString:#"error" options:NSCaseInsensitiveSearch];
if (range.location != NSNotFound) {
// Do thing with range
} else {
// Complain
}
There is nothing whatever wrong with your original test:
if ([XML rangeOfString:#"error" options:NSCaseInsensitiveSearch].location != NSNotFound) {
If all you need to know is whether XML contains the string #"error", that test answers the question and is a perfectly legitimate and idiomatic way to ask it. Observe that even the documentation tells you that containsString: is nothing but a front for calling rangeOfString:options:!
If you really want to know what the positive version would be, it would be to test the length of the returned range and see if it is the same as the length of #"error". The length of a not-found range is 0.
Related
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.
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!");
}
I am using JSON-RPC to communicate between iOS application and the server. Some return values of the server are optional.
Considering the combination of technologies I am using, is it better to return these null values like {"telephone": null} or by completely omitting the "telephone" element in the response?
Futher explanation of what I am asking:
It doesn't look like the JSON-RPC spec specifies much to do with the method result (please correct me if I'm wrong) and clearly not sending loads null elements would improve performance and reduce bandwidth somewhat. What I'm most interested in though is the best approach from an iOS NSJSONSerialization perspective. Is it easier/better to check for whether a key exists in an NSDictionary or whether an existing key has a null value?
I'm using a completely different technique, somewhat unorthodox for sure, since my code is dealing with huge data sets and I really hate having to make tests for NSNull on each value. What I did was subclass NSNull, so when dealing with data, I can test if its numeric value is 0, if its string length is 0, etc.
#implementation NSNull (JSON)
- (NSUInteger)length { return 0; }
- (NSInteger)integerValue { return 0; };
- (CGFloat)floatValue { return 0; };
- (NSString *)description { return #"0(null)"; } // so I know it was NSNull in log messages
- (NSArray *)componentsSeparatedByString:(NSString *)separator { return #[]; }
- (id)objectForKey:(id)key { return nil; }
- (BOOL)boolValue { return NO; }
#end
EDIT: I used this exact same code in an e-commerce shipping app. There were literally thousands of objects getting returned by tens of different APIs - having to look at each item to see if it was [NSNull null] would have been a nightmare. I tried to write a routine to groom results, it would recursively look at dictionaries and arrays and reconstruct the object, but it got too complicated.
In any case, I never even once had an issue with this solution. Obviously YMMV.
First of all {"telephone": "null"} is not a null value. There are quotes so actually telephone property has string value with text "null". Server needs to send {"telephone": null} - without quotes.
I'd go with listing all relevant properties that your app needs in response and put null as a value if there is not value. Then you can easily check in NSDictionary you get from NSJSONSerialization if value for key is NSNull.
if ([dictionaryValue isKindOfClass:[NSNull class]])
myThink.myProperty = nil;
I've packed the check in a function, if anyone interested.
-(id)checkNull:(id)object{
if([object isKindOfClass:[NSNull class]])return nil;
else return object;
}
I am looking for a way to perform a case insensitive string search within another string in Objective-C. I could find ways to search case sensitive strings, and to compare insensitive case, but not searching + case insensitive.
Examples of the search that I would like to perform:
"john" within "i told JOHN to find me a good search algorithm"
"bad IDEA" within "I think its a really baD idea to post this question"
I prefer to stick to only NSStrings.
NSRange r = [MyString rangeOfString:#"Boo" options:NSCaseInsensitiveSearch];
Feel free to encapsulate that into a method in a category over NSString, if you do it a lot. Like this:
#interface NSString(MyExtensions)
-(NSRange)rangeOfStringNoCase:(NSString*)s;
#end
#implementation NSString(MyExtensions)
-(NSRange)rangeOfStringNoCase:(NSString*)s
{
return [self rangeOfString:s options:NSCaseInsensitiveSearch];
}
#end
Your code might become more readable with this. Then again, less readable for those unfamiliar.
mistagged as c#?
heres some advice for objc
NSRange textRange = [[string lowercaseString] rangeOfString:[substring lowercaseString]];
if(textRange.location != NSNotFound)
{
//Does contain the substring
}
which i got from google at this webpage:
http://www.developers-life.com/does-a-nsstring-contain-a-substring.html
does that help your scenario?
If you're using ios 8, you can use NSString's localizedCaseInsensitiveContainsString
- (BOOL)localizedCaseInsensitiveContainsString:(NSString *)aString
localizedCaseInsensitiveContainsString: is the case-insensitive variant. Note that it takes the current locale into effect as well. Locale-independent case-insensitive operation, and other needs can be achieved by calling rangeOfString:options:range:locale: directly.
Here is a self-container solution for Swift:
private func containsKeyword(text: NSString, keyword: String) -> Bool
{
return text.rangeOfString(keyword, options:NSStringCompareOptions.CaseInsensitiveSearch).location != NSNotFound
}
What would be the best method to compare an NSString to a bunch of other strings case insensitive? If it is one of the strings then the method should return YES, otherwise NO.
Here's a little helper function:
BOOL isContainedIn(NSArray* bunchOfStrings, NSString* stringToCheck)
{
for (NSString* string in bunchOfStrings) {
if ([string caseInsensitiveCompare:stringToCheck] == NSOrderedSame)
return YES;
}
return NO;
}
Of course this could be greatly optimized for different use cases.
If, for example, you make a lot of checks against a constant bunchOfStrings you could use an NSSet to hold lower case versions of the strings and use containsObject::
BOOL isContainedIn(NSSet* bunchOfLowercaseStrings, NSString* stringToCheck)
{
return [bunchOfLowercaseStrings containsObject:[stringToCheck lowercaseString]];
}
Just to add a few additions to Nikolai's answer:
NSOrderedSame is defined as 0
typedef NS_ENUM(NSInteger, NSComparisonResult) {NSOrderedAscending = -1L, NSOrderedSame, NSOrderedDescending};
So if you call caseInsensitiveCompare: on a nil object you would get nil. Then you compare nil with NSOrderSame (which is 0) you would get a match which of course is wrong.
Also you will have to check if parameter passed to caseInsensitiveCompare: has to be not nil. From the documentation:
This value must not be nil. If this value is nil, the behavior is
undefined and may change in future versions of OS X.