Check that an email address is valid on iOS [duplicate] - objective-c

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Best practices for validating email address in Objective-C on iOS 2.0?
I am developing an iPhone application where I need the user to give his email address at login.
What is the best way to check if an email address is a valid email address?

Good cocoa function:
-(BOOL) NSStringIsValidEmail:(NSString *)checkString
{
BOOL stricterFilter = NO; // Discussion http://blog.logichigh.com/2010/09/02/validating-an-e-mail-address/
NSString *stricterFilterString = #"^[A-Z0-9a-z\\._%+-]+#([A-Za-z0-9-]+\\.)+[A-Za-z]{2,4}$";
NSString *laxString = #"^.+#([A-Za-z0-9-]+\\.)+[A-Za-z]{2}[A-Za-z]*$";
NSString *emailRegex = stricterFilter ? stricterFilterString : laxString;
NSPredicate *emailTest = [NSPredicate predicateWithFormat:#"SELF MATCHES %#", emailRegex];
return [emailTest evaluateWithObject:checkString];
}
Discussion on Lax vs. Strict - http://blog.logichigh.com/2010/09/02/validating-an-e-mail-address/
And because categories are just better, you could also add an interface:
#interface NSString (emailValidation)
- (BOOL)isValidEmail;
#end
Implement
#implementation NSString (emailValidation)
-(BOOL)isValidEmail
{
BOOL stricterFilter = NO; // Discussion http://blog.logichigh.com/2010/09/02/validating-an-e-mail-address/
NSString *stricterFilterString = #"^[A-Z0-9a-z\\._%+-]+#([A-Za-z0-9-]+\\.)+[A-Za-z]{2,4}$";
NSString *laxString = #"^.+#([A-Za-z0-9-]+\\.)+[A-Za-z]{2}[A-Za-z]*$";
NSString *emailRegex = stricterFilter ? stricterFilterString : laxString;
NSPredicate *emailTest = [NSPredicate predicateWithFormat:#"SELF MATCHES %#", emailRegex];
return [emailTest evaluateWithObject:self];
}
#end
And then utilize:
if([#"emailString#email.com" isValidEmail]) { /* True */ }
if([#"InvalidEmail#notreallyemailbecausenosuffix" isValidEmail]) { /* False */ }

To check if a string variable contains a valid email address, the easiest way is to test it against a regular expression. There is a good discussion of various regex's and their trade-offs at regular-expressions.info.
Here is a relatively simple one that leans on the side of allowing some invalid addresses through: ^[A-Z0-9._%+-]+#[A-Z0-9.-]+\.[A-Z]{2,6}$
How you can use regular expressions depends on the version of iOS you are using.
iOS 4.x and Later
You can use NSRegularExpression, which allows you to compile and test against a regular expression directly.
iOS 3.x
Does not include the NSRegularExpression class, but does include NSPredicate, which can match against regular expressions.
NSString *emailRegex = ...;
NSPredicate *emailTest = [NSPredicate predicateWithFormat:#"SELF MATCHES %#", emailRegex];
BOOL isValid = [emailTest evaluateWithObject:checkString];
Read a full article about this approach at cocoawithlove.com.
iOS 2.x
Does not include any regular expression matching in the Cocoa libraries. However, you can easily include RegexKit Lite in your project, which gives you access to the C-level regex APIs included on iOS 2.0.

Heres a good one with NSRegularExpression that's working for me.
[text rangeOfString:#"^.+#.+\\..{2,}$" options:NSRegularExpressionSearch].location != NSNotFound;
You can insert whatever regex you want but I like being able to do it in one line.

to validate the email string you will need to write a regular expression to check it is in the correct form. there are plenty out on the web but be carefull as some can exclude what are actually legal addresses.
essentially it will look something like this
^((?>[a-zA-Z\d!#$%&'*+\-/=?^_`{|}~]+\x20*|"((?=[\x01-\x7f])[^"\\]|\\[\x01-\x7f])*"\x20*)*(?<angle><))?((?!\.)(?>\.?[a-zA-Z\d!#$%&'*+\-/=?^_`{|}~]+)+|"((?=[\x01-\x7f])[^"\\]|\\[\x01-\x7f])*")#(((?!-)[a-zA-Z\d\-]+(?<!-)\.)+[a-zA-Z]{2,}|\[(((?(?<!\[)\.)(25[0-5]|2[0-4]\d|[01]?\d?\d)){4}|[a-zA-Z\d\-]*[a-zA-Z\d]:((?=[\x01-\x7f])[^\\\[\]]|\\[\x01-\x7f])+)\])(?(angle)>)$
Actually checking if the email exists and doesn't bounce would mean sending an email and seeing what the result was. i.e. it bounced or it didn't. However it might not bounce for several hours or not at all and still not be a "real" email address. There are a number of services out there which purport to do this for you and would probably be paid for by you and quite frankly why bother to see if it is real?
It is good to check the user has not misspelt their email else they could enter it incorrectly, not realise it and then get hacked of with you for not replying. However if someone wants to add a bum email address there would be nothing to stop them creating it on hotmail or yahoo (or many other places) to gain the same end.
So do the regular expression and validate the structure but forget about validating against a service.

Related

Are regexes the right way to extract digits from a ticket number in an NSString?

I would like to programmatically receive a JIRA ticket number, like #"ART-235", and obtain the bare digits / number, #"235".
A question I asked about using regular expressions turned up Regular expressions in an Objective-C Cocoa application with a link to https://developer.apple.com/library/ios/documentation/Foundation/Reference/NSRegularExpression_Class/Reference/Reference.html, and it looks indeed like I can have a regular expression such as \D*?(\d+) and retrieve the value via a regular expression.
However, I wanted to check in and ask if there is a less bletcherous way to do this, or is this an example of why Objective-C is called a bit archaic? The second link gives what looks like everything I need, but it smells a little funny. For the objective stated above, do I want to use regular expressions, or is there a more nicely idiomatic way to perform this sort of string manipulation?
Sounds like -componentsSeparatedByString: would do what you need.
Getting pieces of a fixed, known, format that doesn't use paired delimiters or nesting is exactly the kind of thing that regexes are made to do. I don't see a thing wrong with using one here.
To address your question as written (about "iteration"), however, you might want to look at NSScanner, which does move through the characters of a string by "character class", allowing you to evaluate them as you go.
NSString * ticket = #"ART-235";
NSScanner * scanner = [NSScanner scannerWithString:ticket];
[scanner scanUpToCharactersFromSet:[NSCharacterSet decimalDigitCharacterSet]
intoString:nil];
// As an integer
NSInteger ticketNumber;
[scanner scanInteger:&ticketNumber];
// Or as a string
NSString * ticketNumber;
[scanner scanCharactersFromSet:[NSCharacterSet decimalDigitCharacterSet]
intoString:&ticketNumber];
Like other answers have already said: that simple case can be solved using componentsSeparatedByString:#"-".
That said, your original question is how to enumerate individual characters.
Not all characters are of the same size, some languages combine more than one character into a new language. When enumerating such a string you most likely want to get the resulting of that composition, not the individual pieces. In Objective-C you can enumerate these composed characters like this:
NSString *myString = #"Hello Strings!";
[myString enumerateSubstringsInRange:NSMakeRange(0, myString.length)
options:NSStringEnumerationByComposedCharacterSequences
usingBlock:^(NSString *substring, NSRange substringRange, NSRange enclosingRange, BOOL *stop) {
// Do something with the composed character
NSLog(#"%#", substring);
}];
The example above will log each character one by one.
I made a simple method for you that does the trick, provided that the
ticket identifiers will always be in a "string-number" format !
-(int) numberFromJiraTicket:(NSString*)ticketId
{
//Get number as string
NSString *number = [[ticketId componentsSeparatedByString:#"-"] lastObject];
//Return the INT representation of the number
return [number intValue];
}

Web regex validation on Objective-C

I've some regular expressions for the form fields validation.
I've an unit test to define the expected result
NSArray *suiteWebs = [NSArray arrayWithObjects:
#"http://webapp.stackoverflow.net",
#"http://webapp.stackoverflow.net/info.php",
#"http://www.stackoverflow.net",
#"http://www.stackoverflow.net/",
#"https://webapp.stackoverflow.net",
#"https://webapp.stackoverflow.net/info.php",
#"https://www.stackoverflow.net",
#"https://www.stackoverflow.net/"
#"webapp.stackoverflow.net",
#"webapp.stackoverflow.net/info.php",
#"www.stackoverflow.net",
#"www.stackoverflow.net/",
#"www.stack-overflow.com",
#"www.stackoverflow_.com",
#"www.stackover_flow.com",
nil];
NSArray *falseSuiteWebs = [NSArray arrayWithObjects:
#"ftp://webapp.stackoverflow.net",
#"http:/www.stackoverflow.net",
#"ftps://webapp.stackoverflow.net",
#"https:/www.stackoverflow.net",
nil];
for (NSString *web in suiteWebs) {
NSLog(#"Validating web %#", web);
STAssertTrue([TSAddEntityForm validateWeb:web withPatter:currentRegex], [NSString stringWithFormat:#"currentRegex web %#", web]);
}
for (NSString *web in falseSuiteWebs) {
NSLog(#"Validating web %#", web);
STAssertFalse([TSAddEntityForm validateWeb:web withPatter:currentRegex], [NSString stringWithFormat:#"currentRegex web %#", web]);
}
My actual regular expression is the next one:
NSString *webRegex4 = #"((http|https)://){0,1}((\\w)*|([0-9]*)|([\\-|_])*)+([\\.|/]((\\w)*|([0-9]*)|([\\-|_])*))+";
My problem are with the domains with - my regular expression don't validate it. For example the url www.stack-overflow.com is rejected
Any suggestions?
Thank you
May be this regexp would be better in your case (it's not ideal, but works for above suitable and bad samples):
(http(s)?://)?[\w-]+(\.[\w-]+)*\.\w{2,6}[/\w.-]*
It may begin from http:// or https://,
[\w-]+(\.[\w-]+)*\.\w{2,6} - describes domain
[/\w.-]* - folders and documents
In general, complex regular expressions are fool's gold. Use multiple passes with multiple regular expressions. Validate components of the URLs independently.
Complex regular expressions can be very powerful, but can also paint you into a fragile corner with something as open ended as URLs.
Also, if you're using Objective-C, it is easy to break things down with some of the facilities provided by NSURL.
NSURL will also give you a good idea of what components of a URL you should look at.
By using NSURL methods to extract components of the URLs, you can apply your regular expressions more carefully to each component.
CFURL is equally powerful.

how to validate Zipcode for US or Canada in iOS?

I want to know that is there any way to validate the the zipcode of US or Zipcode of Canada?I have tried to use regex.
Like for US
- (BOOL)validateZip:(NSString *)candidate {
NSString *emailRegex = #"(^{5}(-{4})?$)|(^[ABCEGHJKLMNPRSTVXY][A-Z][- ]*[A-Z]$)";
NSPredicate *emailTest = [NSPredicate predicateWithFormat:#"SELF MATCHES %#", emailRegex];
return [emailTest evaluateWithObject:candidate];
}
but it's not working.Please any body have any idea regarding this validation.if any rest api is there for the validation?Please guide me if possible?
For the US, you have the quantifiers ({5}, {4}, ?) correct but forgot to specify exactly what you're quantifying. You want:
(^[0-9]{5}(-[0-9]{4})?$)
For Canada, according to Wikipedia, the format is A0A 0A0, so I would do:
(^[a-zA-Z][0-9][a-zA-Z][- ]*[0-9][a-zA-Z][0-9]$)
Now, I'd write the complete expression like this, with case insensitivity enabled:
#"^(\\d{5}(-\\d{4})?|[a-z]\\d[a-z][- ]*\\d[a-z]\\d)$"
Frankly, I'm not actually familiar with Objective C or iOS, and sadly I haven't tested the above. However, previously I've seen such posts mention NSRegularExpression, which is missing in your code, but perhaps isn't necessary. Take a look at others' examples to see what other simple errors you might be making. Good luck.
We used this but its only for UK Postcodes. See #acheong87 answer to alter the regex to fit your criteria and other good answers.
NSString *postcodeRegex = #"[A-Z]{1,2}[0-9R][0-9A-Z]?([0-9][ABD-HJLNP-UW-Z]{2}";//WITH SPACES
NSPredict *postcodeValidate = [NSPredicate predicateWithFormat:#"SELF MATCHES %#", postcodeRegex];
if ([postcodeValidate evaluateWithObject:postcodeField.text] == YES) {
NSLog (#"Postcode is Valid");
} else {
NSLog (#"Postcode is Invalid");
}
I advise you test the regex first using this great tool http://www.regexr.com
EDIT
Current regex does not support spaces in postcode after further testing. This will fix that issue.
NSString *postcodeRegex = #"[A-Z]{1,2}[0-9R][0-9A-Z]?(\s|)([0-9][ABD-HJLNP-UW-Z]{2}";//WITH SPACES

Scripting Bridge and filtering SBElementArrays using NSPredicate and FourCharCodes

I'm experimenting with Scripting Bridge for the the first time, but have run into an issue with filtering a SBElementArray according to a NSPredicate containing a FourCharCode enum constant as a criterion.
I wrote a trivial program to identify the "library" source in a user's iTunes library, by using -filteredArrayUsingPredicate: to filter the SBElementArray of all iTunes sources. I was expecting to get back an SBElementArray that, when evaluated, would produce an array of one element, namely the library source. Instead, when I call -get on the returned SBElementArray, I get back an empty array.
Perplexingly, if change the order and instead call -get on the SBElementArray of all sources to get a concrete NSArray, and call -filteredArrayUsingPredicate: on this array with the same predicate as before, I do get the desired result. I don't believe this is supposed to be necessary however, and I've had success filtering a SBElementArray using other NSPredicates (e.g. #"name=='Library'" works fine).
The code snippet is below. iTunesESrcLibrary is a FourCharCode constant defined in the header file generated by Scripting Bridge. (iTunesESrcLibrary = 'kLib'). I'm running 10.6.5.
iTunesApplication* iTunes = [[SBApplication alloc] initWithBundleIdentifier:#"com.apple.iTunes"];
NSPredicate* libraryPredicate = [NSPredicate predicateWithFormat:#"kind == %u", iTunesESrcLibrary];
SBElementArray* allSources_Attempt1 = [iTunes sources];
SBElementArray* allLibrarySources_Attempt1 = (SBElementArray*)[allSources_Attempt1 filteredArrayUsingPredicate:libraryPredicate];
NSLog(#"Attempt 1: %#", allLibrarySources_Attempt1);
NSLog(#"Attempt 1 (evaluated): %#", [allLibrarySources_Attempt1 get]);
NSArray* allSources_Attempt2 = [[iTunes sources] get];
NSArray* allLibrarySources_Attempt2 = [allSources_Attempt2 filteredArrayUsingPredicate:libraryPredicate];
NSLog(#"Attempt 2: %#", allLibrarySources_Attempt2);
The output I get is the following:
Attempt 1: <SBElementArray #0x3091010: ITunesSource whose 'cmpd'{ 'relo':'= ', 'obj1':'obj '{ 'want':'prop', 'from':'exmn'($$), 'form':'prop', 'seld':'pKnd' }, 'obj2':1800169826 } of application "iTunes" (88827)>
Attempt 1 (evaluated): (
)
Attempt 2: (
"<ITunesSource #0x3091f10: ITunesSource id 65 of application \"iTunes\" (88827)>"
)
I think I've figured it out. It seems you can't simply use a FourCharCode's integer value directly in a NSPredicate that you intend to use to filter an SBElementArray.
By chance, I found that instead of:
[NSPredicate predicateWithFormat:#"kind == %u", iTunesESrcLibrary]
you need to use:
[NSPredicate predicateWithFormat:#"kind == %#", [NSAppleEventDescriptor descriptorWithTypeCode: iTunesESrcLibrary]]
Using this second form, I can filter the SBElementArray sources list as expected. However, this new predicate can't be used to filter the NSArray, even though this array is just the evaluated form of the SBElementArray! Here, you have to switch back to the %u version.
Rant:
Frankly this sucks, and it seems the sort of thing Scripting Bridge should deal with so I don't have to; I shouldn't have to know what an NSAppleEventDescriptor is. And while it's reasonable that not all predicates that work with NSArray should work with SBElementArray, the converse should not be the case and it's unnecessarily confusing that it is.

MAC OS X: How to determine if filesystem is case sensitive?

I have used the statfs(2) system call to get many characteristics of a Mac OS X filesystem, but it doesn't tell me if the filesystem is case-sensitive or not.
I need this information as the application I am developing will be moving many files around and I want to detect potential loss of data due to files being moved from a case-sensitive filesystem to a case-insensitive filesystem.
Can anyone suggest a way of detecting this?
If you're already using stat(2), then you can easily use pathconf(2) with the _PC_CASE_SENSITIVE selector (result 0 = case-insensitve, 1 = case-sensitive. Note that the man page is out of date, but the _PC_CASE_SENSITIVE and _PC_CASE_PRESERVING are supported. By convention, if a file system doesn't support _PC_CASE_SENSITIVE selector then it is case-sensitive.
I’ve looked around and haven’t found an API for that. There are two possibilities I can think of:
Creating a temporary file and trying to open it with a different case pattern, e.g. creating "a9999" and trying to open "A9999". Considering that neither "a9999" nor "A9999" were available on that particular directory, the filesystem is case-sensitive if and only if opening "A9999" fails.
Running diskutil(8) against the filesystem. It reports case-sensitive, -insensitive file systems differently: Name: Mac OS Extended (Case-sensitive) vs. Name: Mac OS Extended (not journaled).
Since diskutil(8) is able to identify that, it could be the case that this information is available via some API or system call.
Edit: It turns out that NSURL has a set of methods that work on file system properties. In particular, -getResourceValue:forKey:error with the key being NSURLVolumeSupportsCaseSensitiveNamesKey will tell you whether a given filesystem (represented as an NSURL instance) supports case sensitive names.
See the following code for an example of its use.
#include <Foundation/Foundation.h>
int main(int argc, char *argv[]) {
NSAutoreleasePool *pool = [NSAutoreleasePool new];
NSString *path = [NSString stringWithCString:argv[1] encoding:NSASCIIStringEncoding];
NSURL *filesystem = [NSURL fileURLWithPath:path isDirectory:YES];
NSNumber *caseSensitiveFS;
BOOL hasCaseSensitiveResource;
hasCaseSensitiveResource = [filesystem getResourceValue:&caseSensitiveFS
forKey:NSURLVolumeSupportsCaseSensitiveNamesKey error:NULL];
if (hasCaseSensitiveResource)
{
if ([caseSensitiveFS intValue] == 1)
{
NSLog(#"%s is a case sensitive filesystem", argv[1]);
}
else
{
NSLog(#"%s is a case insensitive filesystem", argv[1]);
}
}
else
{
NSLog(#"can't query %s for case sensitiveness", argv[1]);
}
[pool drain];
return 0;
}
Output example:
./testcase /
/ is a case insensitive filesystem
./testcase /Volumes/Disk\ Image/
/Volumes/Disk Image/ is a case sensitive filesystem
./testcase nonono
can't query nonono for case sensitiveness
Create a temporary file with uppercase letters and check if the file exists using lowercase letters, if the test fails the file system is case-sensitive.
Look here for some code to find the HFS subtype of a device:
http://www.opensource.apple.com/source/libfs/libfs-3/FSFormatName.c
The routine is_hfs will return the hfs subtype. If the subtype is kHFSXSubType or kHFSXJSubType, then it's an HFSX (case sensitive) device.