"The alternative is to send my users back to earlier view controllers if parse gives me an error back after method call (which I do not want to do)"
Note - Unfortunately, you simply won't be able to build parse apps unless you "send them back" like that. Unfortunately "it's that simple." Quite literally every single such "step" when dealing with Parse, you have to be able to "go back" in the sense you describe.
In answer to your question, as you probably know essentially THERE IS NO really good way to truly check if a string is an email, due to various problems with the nature of defining an email, and the fact that you simply don't actually want the full set of "really possible" email strings, for any app.
In practice the following category works well.
It's in use in many high volume production apps.
Note that NSPredicate is, I feel, the most natural, reliable way to do this in iOS.
-(BOOL)basicLocalEmailCheck
{
if ( self.length > 50 ) return NO;
// note, first if it ends with a dot and one letter - that is no good
// (the regex below from W3C does allow a final single-letter tld)
NSString *rx = @".*\\..$";
NSPredicate *emailTest = [NSPredicate
predicateWithFormat:@"SELF MATCHES %@", rx];
if ( [emailTest evaluateWithObject:self] ) return NO;
// here's the original from the W3C HTML5 spec....
// ^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$
// i made a modification,
// you can't have eg "localhost" with no .com,
// and note you have to escape one backslash for the string from the W3C
rx = @"^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?){1,5}$";
emailTest = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", rx];
return [emailTest evaluateWithObject:self];
}
If you are a beginner and not familiar with categories, it's a good opportunity to use one.
Here are some typical real-world uses...particularly relating to Parse since you mention that.
-(IBAction)joinNow
{
[self.view endEditing:YES];
if ( [self _fieldBlank] )
{ [self woe:@"Please complete all fields."]; return; }
if ( ! [self.email.text basicLocalEmailCheck] )
{ [self woe:@"Please enter a valid email."]; return; }
if ( self.imageHasBeenSet == NO )
{ [self woe:@"Please add a picture."]; return; }
if ( self.password.text.length > 20 ||
self.firstname.text.length > 20 ||
self.surname.text.length > 20 )
{ [self woe:@"20 character limit for names and password."]; return; }
[self ageConfirmThenJoin];
}
-(IBAction)clickLogin:(id)sender
{
[self.view endEditing:YES];
[PFUser logOut];
if ( ! [self.loginEmail.text basicLocalEmailCheck] )
{
[UIAlertView ok:@"Please enter your email in the email field."];
[self begin];
return;
}
[APP huddie];
APP.hud.labelText = @"Logging in ...";
[PFAnalytics trackEvent:@"loginAttempt"];
[PFUser logInWithUsernameInBackground: [self.loginEmail.text lowercaseString]
password: self.loginPassword.text
block:^(PFUser* user, NSError* error)
{
[APP.hud hide:YES];
if (user) // Login successful
{
[PFAnalytics trackEvent:@"loginSuccess"];
[self isLoggedInCheckValid];
return;
}
else
{
// note, with Parse it SEEMS TO BE THE CASE that
// 100, no connection 101, bad user/pass
NSString *msg;
NSString *analyticsMsg = @"otherProblem";
if ( !error)
{
msg = @"Could not connect. Try again later...";
// seems unlikely/impossible this could happen
}
else
{
if ( [error code] == 101 )
{
msg = @"Incorrect email or password. Please try again.";
analyticsMsg = @"passwordWrong";
}
else
{
msg = @"Could not connect. Try again later.";
}
}
[PFAnalytics trackEvent:@"loginFailure"
dimensions:@{ @"reason":analyticsMsg }];
[UIAlertView ok:msg];
[self begin]; // not much else we can do
return;
}
}];
}