NSTextCheckingResult for phone numbers - iphone

Can someone tell me why this evaluates every time to true?!
The input is: jkhkjhkj. It doesn't matter what I type into the phone field. It's every time true...
NSRange range = NSMakeRange (0, [phone length]);
NSTextCheckingResult *match = [NSTextCheckingResult phoneNumberCheckingResultWithRange:range phoneNumber:phone];
if ([match resultType] == NSTextCheckingTypePhoneNumber)
{
return YES;
}
else
{
return NO;
}
Here is the value of match:
(NSTextCheckingResult *) $4 = 0x0ab3ba30 <NSPhoneNumberCheckingResult: 0xab3ba30>{0, 8}{jkhkjhkj}
I was using RegEx and NSPredicate but I've read that since iOS4 it's recommended to use NSTextCheckingResult but I can't find any good tutorials or examples on this.
Thanks in advance!

You are using the class incorrectly. NSTextCheckingResult is the result of a text checking that is done by NSDataDetector or NSRegularExpression. Use NSDataDetector instead:
NSError *error = NULL;
NSDataDetector *detector = [NSDataDetector dataDetectorWithTypes:NSTextCheckingTypePhoneNumber error:&error];
NSRange inputRange = NSMakeRange(0, [phone length]);
NSArray *matches = [detector matchesInString:phone options:0 range:inputRange];
// no match at all
if ([matches count] == 0) {
return NO;
}
// found match but we need to check if it matched the whole string
NSTextCheckingResult *result = (NSTextCheckingResult *)[matches objectAtIndex:0];
if ([result resultType] == NSTextCheckingTypePhoneNumber && result.range.location == inputRange.location && result.range.length == inputRange.length) {
// it matched the whole string
return YES;
}
else {
// it only matched partial string
return NO;
}

Related

how to write NSRegularExpression to xx:xx:xx?

i am trying to check if NSString is in specific format. dd:dd:dd. I was thinking of NSRegularExpression. Something like
/^(\d)\d:\d\d:\d\d)$/ ?
Have you tried something like:
NSError *error = NULL;
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:#"^\d{2}:\d{2}:\d{2}$"
options:0
error:&error];
NSUInteger numberOfMatches = [regex numberOfMatchesInString:string
options:0
range:NSMakeRange(0, [string length])];
(I haven't tested it, because I cannot right now, but it should be working)
I suggest to use RegexKitLite
With this and assuming that in dd:dd:dd 'd' actually stands for a digit from 0-9 it should be fairly easy to implement what you need given the additional comment from Grijesh.
Here's an example copied from the RegexKitLite page:
// finds phone number in format nnn-nnn-nnnn
NSString *regEx = #"{3}-[0-9]{3}-[0-9]{4}";
NSString *match = [textView.text stringByMatching:regEx];
if ([match isEqual:#""] == NO) {
NSLog(#"Phone number is %#", match);
} else {
NSLog(#"Not found.");
}
UPDATE:
NSString *idRegex = #"[0-9][0-9]:[0-9][0-9]:[0-9][0-9]";
NSPredicate *idTest = [NSPredicate predicateWithFormat:#"SELF MATCHES %#", idRegex];
for (NSString * str in newArrAfterPars) {
if ([idTest evaluateWithObject:str]) {
}
}

How to check if a string contains an URL

i have text message and I want to check whether it is containing text "http" or URL exists in that.
How will I check it?
NSString *string = #"xxx http://someaddress.com";
NSString *substring = #"http:";
Case sensitive example:
NSRange textRange = [string rangeOfString:substring];
if(textRange.location != NSNotFound){
//Does contain the substring
}else{
//Does not contain the substring
}
Case insensitive example:
NSRange textRange = [[string lowercaseString] rangeOfString:[substring lowercaseString]];
if(textRange.location != NSNotFound){
//Does contain the substring
}else{
//Does not contain the substring
}
#Cyprian offers a good option.
You could also consider using a NSRegularExpression which would give you far more flexibility assuming that's what you need, e.g. if you wanted to match http:// and https://.
Url usually has http or https in it
You can use your custom method containsString to check for those strings.
- (BOOL)containsString:(NSString *)string {
return [self containsString:string caseSensitive:NO];
}
- (BOOL)containsString:(NSString*)string caseSensitive:(BOOL)caseSensitive {
BOOL contains = NO;
if (![NSString isNilOrEmpty:self] && ![NSString isNilOrEmpty:string]) {
NSRange range;
if (!caseSensitive) {
range = [self rangeOfString:string options:NSCaseInsensitiveSearch];
} else {
range = [self rangeOfString:string];
}
contains = (range.location != NSNotFound);
}
return contains;
}
Example :
[yourString containsString:#"http"]
[yourString containsString:#"https"]

How to search for NSMutablearray content in substring of a NSString?

I created an NSMutableArray and am searching/matching the content of array with NSString's substring but how to do it?
NSMutableArray *tmparray = {#"PPG" , #"AG" , #"HD" };
NSString *tmpstring = #"MAAPPG";
if ([tmparray containsObject:tmpstring] ) {
NSLog(#"String found");
}
But it's not happening because tmparray contain "PPG" not "MAAPPG" how to search for substring matching ?
You can use an NSPredicate to do this, but it may be slow. Here's a simpler approach along the lines of what your trying:
NSMutableArray *tmparray = {#"PPG" , #"AG" , #"HD" };
NSString *tmpstring = #"MAAPPG";
NSRange *tmprange;
for(NSString *string in tmparray) {
tmprange = [tmpstring rangeOfString:string];
if (tmprange.location != NSNotFound) {
NSLog(#"String found");
break;
}
}
how about this
for(NSString *arrString in tmparray){
if([tmpstring rangeOfString:arrString].location != NSNotFound){
NSLog(#"String found");
}
}
NSArray *tmparray = [NSArray arrayWithObjects:#"PPG" , #"AG" , #"HD",nil];
NSString *tmpstring = #"MAAPPG";
NSRange textRange;
for(NSString *string in tmparray)
{
textRange =[tmpstring rangeOfString:string];
if(textRange.location != NSNotFound)
{
NSLog(#"String found ");
}
}
check this by using other substrings also...

Sorting an NSArray like the MPMediaPickerController/iPod Library

I'm developing a custom UIViewController for iPhone that emulates a subset of the MPMediaPickerController for files in my application's local documents directory. In particular, I'm attempting to re-create the Songs tab. I've been successful in creating my new controller, except I can't get the song titles to sort like they do in the iPod Library or the MPMediaPickerController. Here's an example of how the song names need to be sorted:
Awesome Song Title
Cool Song
The Darkest Song Ever
My Song Title
A Really Cool Song
Why Me?
4 Hours Wasted
As you can see, the sorting excludes the leading articles in the song titles and also places songs that begin with a numeric value at the end of the list. Can anyone suggest an efficient sorting function that takes these tules into account?
Since it looks like no one could offer up a solution, I thought I would post the solution I came up with. First, I created a model for my data:
#interface MyModel : NSObject
{
NSString* _value;
NSString* _sortableValue;
}
#property (nonatomic,copy) NSString* value;
- (NSString*)sortableValue;
- (NSString*)comparableString:(NSString*)str;
#end
They key to the model is the comparableString method, which gets used to create the sortableValue. Here's the implementation of the model:
#implementation MyModel
#synthesize value=_value;
-(void)dealloc
{
[_value release];
[_sortableValue release];
[super dealloc];
}
- (void)setValue:(NSString*)value
{
[_value release];
_value = [value copy];
[_sortableValue release];
_sortableTValue = nil;
}
- (NSString*)sortableValue
{
if (_sortableValue == nil)
_sortableValue = [[self comparableString:_value] retain];
return _sortableValue;
}
- (NSString*)comparableString:(NSString*)str
{
if (str == nil)
return nil;
else if ([str length] == 0)
return [NSString stringWithString:str];
NSCharacterSet* numbersSet = [NSCharacterSet decimalDigitCharacterSet];
if ([str rangeOfCharacterFromSet:numbersSet options:0 range:NSMakeRange(0, 1)].location != NSNotFound)
return [NSString stringWithString:str];
NSRange range = NSMakeRange(0, [str length]);
if ([str compare:#"a " options:(NSAnchoredSearch|NSCaseInsensitiveSearch) range:NSMakeRange(0, 2)] == NSOrderedSame)
range.location = 2;
else if ([str compare:#"an " options:(NSAnchoredSearch|NSCaseInsensitiveSearch) range:NSMakeRange(0, 3)] == NSOrderedSame)
range.location = 3;
else if ([str compare:#"the " options:(NSAnchoredSearch|NSCaseInsensitiveSearch) range:NSMakeRange(0, 4)] == NSOrderedSame)
range.location = 4;
range.length -= range.location;
NSCharacterSet* lettersSet = [NSCharacterSet letterCharacterSet];
NSUInteger letterOffset = [str rangeOfCharacterFromSet:lettersSet options:0 range:range].location;
if (letterOffset == NSNotFound)
return [NSString stringWithString:str];
letterOffset -= range.location;
range.location += letterOffset;
range.length -= letterOffset;
return [str substringWithRange:range];
}
#end
In addition to the removing the leading articles from the string, it also removes any leading non-letter characters. I have a song in my iPod library entitled "$ell Your $oul", which ends up in the E section in the MPMediaPickerController. I'm not sure that's what I would have done had I crated the initial sorting algorithm, but I was going to consistency with the MPMediaPickerController, so there you go.
The final piece of the puzzle is the UILocalizedIndexedCollation class. This handy little helper class will help you sort your data to make supplying it to a UITableView via a UITableViewDataSource a piece of cake. Here's a snippet on how to use the UILocalizedIndexedCollation class in conjunction with the model:
// tableData will contain an NSArray for each populated section in the table view
NSMutableDictionary* tableData = [NSMutableDictionary dictionary];
NSMutableArray* myArray = [NSMutableArray array];
// Populate myArray with instances of MyModel
UILocalizedIndexedCollation* indexer = [UILocalizedIndexedCollation currentCollation];
for (MyModel* data in myArray)
{
NSInteger index = [indexer sectionForObject:data collationStringSelector:#selector(sortableValue)];
NSNumber* key = [[NSNumber alloc] initWithInteger:index];
NSMutableArray* array = [tableData objectForKey:key];
if (array == nil)
{
array = [NSMutableArray new]; // Will be released after creating a sorted array in the following section
[tableData setObject:array forKey:key];
}
[array addObject:data];
[key release];
}
[tableData enumerateKeysAndObjectsUsingBlock:^(id key, id array, BOOL* stop)
{
NSMutableArray* sortedArray = [[indexer sortedArrayFromArray:array collationStringSelector:#selector(sortableValue)] mutableCopy];
[tableData setObject:sortedArray forKey:key];
[array release];
}];
One quick note about UILocalizedIndexedCollation (from Apple's documentation):
If the application provides a
Localizable.strings file for the
current language preference, the
indexed-collation object localizes
each string returned by the method
identified by selector.
So make sure you provide a Localizable.strings for each language you want to support, or your table view will only have sections A-Z and #.
It took me a while to work out all of the details on this, so I hope it becomes useful for other people. If you see any ways I can improve this, please let me know!
You may need to consider certain characters with accents as well such as è, é, ò, à, ù, ì.
So I slightly modified your code to incorporate this. Your code is a great contribution to all of us iphone developers
- (NSString*)comparableString:(NSString*)str
{
if (str == nil)
return nil;
else if ([str length] == 0)
return [NSString stringWithString:str];
NSCharacterSet* numbersSet = [NSCharacterSet decimalDigitCharacterSet];
if ([str rangeOfCharacterFromSet:numbersSet options:0 range:NSMakeRange(0, 1)].location != NSNotFound)
return [NSString stringWithString:str];
NSRange range = NSMakeRange(0, [str length]);
if ([str compare:#"a " options:(NSAnchoredSearch|NSCaseInsensitiveSearch) range:NSMakeRange(0, 2)] == NSOrderedSame)
range.location = 2;
else if ([str compare:#"an " options:(NSAnchoredSearch|NSCaseInsensitiveSearch) range:NSMakeRange(0, 3)] == NSOrderedSame)
range.location = 3;
else if ([str compare:#"the " options:(NSAnchoredSearch|NSCaseInsensitiveSearch) range:NSMakeRange(0, 4)] == NSOrderedSame)
range.location = 4;
range.length -= range.location;
NSCharacterSet* lettersSet = [NSCharacterSet letterCharacterSet];
NSUInteger letterOffset = [str rangeOfCharacterFromSet:lettersSet options:0 range:range].location;
if (letterOffset == NSNotFound)
return [NSString stringWithString:str];
letterOffset -= range.location;
range.location += letterOffset;
range.length -= letterOffset;
//my modification starts here.........
NSString * finalString = [str substringWithRange:range];
NSString * firstCharString = [finalString substringToIndex:1];
NSData * encodedData = [firstCharString dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES];
NSString * encodedString = [[NSString alloc] initWithBytes:[encodedData bytes] length:[encodedData length] encoding:NSASCIIStringEncoding];
if ([encodedString isEqualToString:#"?"]) {
return finalString;
}
NSString * finalProcessedString = [finalString stringByReplacingCharactersInRange:NSMakeRange(0, 1) withString:encodedString];
[encodedString release];
return finalProcessedString;
}

iPhone 'Whole Word' Search

I am currently using the following algorithm to search on my iPhone app:
NSRange range = [entry.englishEntry rangeOfString:searchText options:NSCaseInsensitiveSearch];
if(range.location != NSNotFound)
{
[self.filteredListContent addObject:entry];
}
The problem is that when I search for a word like 'crap' I also get results for words like 'scrap' which is irrelevant. I am unfamiliar with NSRange so what is the search algorithm for searching the whole word?
I just solved this problem by adding a simple category on NSString to do a word boundary search. Here's the code:
#interface NSString (FullWordSearch)
// Search for a complete word. Does not match substrings of words. Requires fullWord be present
// and no surrounding alphanumeric characters.
- (BOOL)containsFullWord:(NSString *)fullWord;
#end
#implementation NSString (FullWordSearch)
- (BOOL)containsFullWord:(NSString *)fullWord {
NSRange result = [self rangeOfString:fullWord];
if (result.length > 0) {
if (result.location > 0 && [[NSCharacterSet alphanumericCharacterSet] characterIsMember:[self characterAtIndex:result.location - 1]]) {
// Preceding character is alphanumeric
return NO;
}
if (result.location + result.length < [self length] && [[NSCharacterSet alphanumericCharacterSet] characterIsMember:[self characterAtIndex:result.location + result.length]]) {
// Trailing character is alphanumeric
return NO;
}
return YES;
}
return NO;
}
#end
Yes you can search within words. You need to split the string into components first. Then loop through each one and compare them.
Something like that:
NSArray *words = [entry.english componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
for (NSString *word in words)
{
NSComparisonResult result = [word compare:searchText options:(NSCaseInsensitiveSearch|NSDiacriticInsensitiveSearch) range:NSMakeRange(0, [searchText length])];
if (result == NSOrderedSame)
{
[self.filteredListContent addObject:entry];
break;
}
}
Instead of finding the range of a string, just do a case-insensitive compare and check if the result is NSOrderedSame
if([entry.english caseInsensitiveCompare:searchText] == NSOrderedSame){
[self.filteredListContent addObject:entry];
}
This will compare the text with the whole word and not just look for the range.
Now i make it as more generic, by using this code you can search any string between target string
NSString * strName =[entry.english lowercaseString];
if ([strName rangeOfString:[searchText lowercaseString]].location != NSNotFound) {
[self.filteredListContent addObject:entry];}