This seems to be an iOS8 only issue.
I have an app that uses the NSURLConnection delegate. Everything works exactly as it should, except in iOS8 when multiple connections are created and ran successively, an arbitrary number of connections fail to authenticate.
- (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
{
if (challenge.previousFailureCount == 0)
{
KeychainItemWrapper *itemWrapper = [[KeychainItemWrapper alloc] initWithIdentifier:#"login" accessGroup:nil];
[[challenge sender] useCredential:[NSURLCredential credentialWithUser:[itemWrapper objectForKey:(__bridge id)(kSecAttrAccount)] password:[itemWrapper objectForKey:(__bridge id)(kSecValueData)] persistence:NSURLCredentialPersistenceNone] forAuthenticationChallenge:challenge];
_authenticationChallenged = YES;
}
else
{
[self cancel];
KeychainItemWrapper *itemWrapper = [[KeychainItemWrapper alloc] initWithIdentifier:#"login" accessGroup:nil];
if ([[itemWrapper objectForKey:(__bridge id)(kSecAttrAccount)] isEqualToString:#""]) {
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Please Log In" message:#"To use this app, please log in" delegate:self cancelButtonTitle:#"Cancel" otherButtonTitles:#"Log In", nil];
[alertView setTag:2];
[alertView setAlertViewStyle:UIAlertViewStyleLoginAndPasswordInput];
[alertView show];
} else {
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Invalid Credentials" message:nil delegate:self cancelButtonTitle:#"Cancel" otherButtonTitles:#"Log In", nil];
[alertView setTag:2];
[alertView setAlertViewStyle:UIAlertViewStyleLoginAndPasswordInput];
[alertView show];
}
}
}
In above code is where the issue lies. The else statement is getting called, even though the credentials are correct and work correctly on every other request. This only happens when I create multiple (separate) NSURLConnections at the same time and run them.
Any ideas?
Thanks!
Related
I am using this code for Update Email Address and Forgot Password but their is a problem when I click on 'ForgotPassword' button it's work properly but when I click on 'UpdateEmail' button it not work properly it call the UIAlert for 'ForgotPassword' button and I am trying to call" else if (self.ForgotPassword.tag == 1) part of -(Void)alertView " for when I press 'UpdateEmail' UIButton.
//Forgot method for ForgotPassword
-(IBAction)ForgotPassword:(id)sender
{
UIAlertView * forgotPassword=[[UIAlertView alloc] initWithTitle:#"Forgot Password" message:#"Please enter your email id" delegate:self cancelButtonTitle:#"Cancel" otherButtonTitles:#"Ok", nil];
forgotPassword.alertViewStyle=UIAlertViewStylePlainTextInput;
[forgotPassword textFieldAtIndex:0].delegate=self;
[forgotPassword show];
}
//Method for Update Email Address
-(IBAction)UpdateEmail:(id)sender
{
if ([PFUser currentUser])
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Update Email"
message:#"Enter Your Email Address"
delegate:self
cancelButtonTitle:#"Cancel"
otherButtonTitles:#"Ok",nil];
alert.alertViewStyle = UIAlertViewStylePlainTextInput;
[alert show];
}
else
{
UIAlertView *myAlert1 = [[UIAlertView alloc]
initWithTitle:#"Please First Loginig"
message:#"Please First Loging"
delegate:nil
cancelButtonTitle:#"Cancel"
otherButtonTitles:#"Ok",nil];
[myAlert1 show];
}
}
// Method for Alert View
-(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{
self.ForgotPassword.tag=0;
self.UpdateEmail.tag=1;
if (self.ForgotPassword.tag == 0){
if(buttonIndex ==1){
NSLog(#"ok button clicked in forgot password alert view");
NSString *femailId=[alertView textFieldAtIndex:0].text;
if ([femailId isEqualToString:#""]){
UIAlertView *display;
display=[[UIAlertView alloc] initWithTitle:#"Email" message:#"Please enter password for resetting password" delegate:nil cancelButtonTitle:#"Ok" otherButtonTitles: nil];
[display show];
}else{
[PFUser requestPasswordResetForEmailInBackground:femailId block:^(BOOL succeeded, NSError *error){
UIAlertView *display;
if(succeeded){
display=[[UIAlertView alloc] initWithTitle:#"Password email" message:#"Please check your email for resetting the password" delegate:nil cancelButtonTitle:#"Ok" otherButtonTitles: nil];
}else{
display=[[UIAlertView alloc] initWithTitle:#"Email" message:#"Email doesn't exists in our database" delegate:nil cancelButtonTitle:#"Cancel" otherButtonTitles: nil];
}
[display show];
}];
}
}
}else if (self.ForgotPassword.tag == 1){
PFUser *user = [PFUser currentUser];
user[#"email"] = [alertView textFieldAtIndex:0].text;
[user saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error){
if (succeeded){
UIAlertView *myAlert1 = [[UIAlertView alloc]
initWithTitle:#"Email Upadated!"
message:#"your Email is Updated"
delegate:nil
cancelButtonTitle:#"Cancel"
otherButtonTitles:#"Ok",nil];
[myAlert1 show];
//NSLog(#"Success");
}else{
UIAlertView *myAlert1 = [[UIAlertView alloc]
initWithTitle:#"Email is NOT Update"
message:#"Email is alredy registred"
delegate:nil
cancelButtonTitle:#"Cancel"
otherButtonTitles:#"Ok",nil];
[myAlert1 show];
NSLog(#"Error");
}
}];
}
}
You need to give tag to your two different UIAlertView like below.
-(IBAction)ForgotPassword:(id)sender
{
UIAlertView * forgotPassword=[[UIAlertView alloc] initWithTitle:#"Forgot Password" message:#"Please enter your email id" delegate:self cancelButtonTitle:#"Cancel" otherButtonTitles:#"Ok", nil];
forgotPassword.alertViewStyle=UIAlertViewStylePlainTextInput;
[forgotPassword textFieldAtIndex:0].delegate=self;
[forgotPassword show];
forgotPassword.tag = 0; //// Here for forgot password
}
-(IBAction)UpdateEmail:(id)sender
{
if ([PFUser currentUser])
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Update Email"
message:#"Enter Your Email Address"
delegate:self
cancelButtonTitle:#"Cancel"
otherButtonTitles:#"Ok",nil];
alert.alertViewStyle = UIAlertViewStylePlainTextInput;
[alert show];
alert.tag =1; ///Here for email update
}
}
Then, in -(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex, you can detect which alertView's button was clicked.
-(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{
{
if(alertView.tag == 0) /// Because we assigned forgotPassword.tag = 0; above for forgotPassword
{
if(buttonIndex == YOUR_DESIRED_BUTTON_INDEX)
{
///Your code for Forgot Password.
}
}
else if(alertView.tag ==1) /// Because we assigned alert.tag = 1; above for update email
{
if(buttonIndex == YOUR_DESIRED_BUTTON_INDEX)
{
///Your code for Update Email.
}
}
}
An async request has been sent to the server and here is my connection delegate.
in RKYLoginDelegate.m file, i made an alert to tell user that the member is verifying when receiving data.
didReceivedData
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
[_receivedData appendData:data];
loginAlertView = [[UIAlertView alloc] initWithTitle:#"message"
message:#"verifying member..."
delegate:self
cancelButtonTitle:nil
otherButtonTitles:nil];
[loginAlertView performSelectorOnMainThread:#selector(show) withObject:nil waitUntilDone:YES];
NSLog(#"Received data: %#", [[NSString alloc] initWithData:_receivedData encoding:NSUTF8StringEncoding]);
}
and in finish loading data, if nothing return, then shows the error message.
didFinishLoading
- (void)connectionDidFinishLoading:(NSURLConnection *)connection;
in the code, it will verify return state and parse value, if not respond then alert a dialog to notify user error message
if ([jsonDataDictionary count] > 0) {
// add member into data
RKYMemberManager *rkyMemberManager = [RKYMemberManager new];
[rkyMemberManager addMember:jsonDataDictionary];
// navigate to main
UIStoryboard *rkyMainStoryboard = [UIStoryboard storyboardWithName:#"RKYMainStoryboard" bundle:nil];
RKYMainViewController *rkyMainViewController =
[rkyMainStoryboard instantiateViewControllerWithIdentifier:#"RKYMain"];
[[[UIApplication sharedApplication] delegate].window.rootViewController.navigationController presentViewController:rkyMainViewController animated:YES completion:nil];
}
else {
UIAlertView *alertView = [[UIAlertView alloc]
initWithTitle:#"message"
message:#"Cannot login!"
delegate:self
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alertView performSelectorOnMainThread:#selector(show) withObject:nil waitUntilDone:YES];
[alertView show];
NSLog(#"cannot login");
}
Those alert dialogs did show its message, but will cause an error:
Thread 1:EXC_BAD_ACCESS(code=2, address=0xc)
as title, am I doing correctly?
if yes, how to solve the problem that caused?
I've written a simple app which validates user input (whether NULL or longer than a define length). It should return validation error messages when validation fails and otherwise, redirect to another page.
However, the app only returns the messge for the first condition (Username is Empty) for all scenarions. (Such as username is filled and password is empty, etc.)
m file:
- (IBAction)doLogin {
if(uname.text==NULL) {
UIAlertView *err1 = [[UIAlertView alloc]
initWithTitle:#"Required field!" message:#"Username is empty." delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[err1 show];
NSLog(#"%#",uname.text);
}
else if(passw.text==NULL) {
UIAlertView *err2 = [[UIAlertView alloc]
initWithTitle:#"Required field!" message:#"Password is empty." delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[err2 show];
NSLog(#"%#",passw.text);
}
else if (uname.text.length < 6)
{
UIAlertView *err3 = [[UIAlertView alloc]
initWithTitle:#"Invalid!" message:#"Enter a username longer than 6 chars." delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[err3 show];
NSLog(#"%#",uname.text);
}
else if (uname.text.length < 8)
{
UIAlertView *err4 = [[UIAlertView alloc]
initWithTitle:#"Invalid!" message:#"Enter a password longer than 8 chars." delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[err4 show];
NSLog(#"%#",uname.text);
}
else {
/*UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"" message:#"Thank you" delegate:self cancelButtonTitle:#"Close" otherButtonTitles:#"OK", nil];
[alert show];*/
UIViewController* flipViewController = [[UIViewController alloc] initWithNibName:#"flip" bundle:[NSBundle mainBundle]];
[self.view addSubview:flipViewController.view];
}
An alternative to karthika (but using similar structure) this will provide feedback on the entire form in a single message. Perhaps a little more user friendly and certainly reduces negative user interaction.
-(BOOL)isFormDataValid{
NSMutableArray *errorMessages = [[NSMutableArray alloc] init];
if([self.emailTextField.text isEqualToString:#""])
{
[errorMessages addObject:NSLocalizedString(#"Please enter email",nil)];
}
if([self.passwordTextField.text isEqualToString:#""])
{
[errorMessages addObject:NSLocalizedString(#"Please enter password",nil)];
}
if ([errorMessages count]) {
NSString * msgs = [errorMessages componentsJoinedByString:#"\n"];
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:NSLocalizedString(#"Whoops!",nil) message:msgs delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alert show];
return NO;
} else {
return YES;
}
}
-(BOOL)isFormDataValid{
NSString *errorMessage = nil;
UITextField *errorField;
if([nameTextField.text isEqualToString:#""])
{
errorMessage = #"Please enter username";
errorField = nameTextField;
}
else if([[nameTextField.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]] length]==0)
{
errorMessage = #"white spaces not allowed";
errorField = nameTextField;
}
else if([passwordTextField.text isEqualToString:#""])
{
errorMessage = #"Please enter password";
errorField = passwordTextField;
}
else if([[passwordTextField.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]] length]==0)
{
errorMessage = #"white spaces not allowed";
errorField = passwordTextField;
}
if (errorMessage) {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Failed!" message:errorMessage delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alert show];
[alert release];
[errorField becomeFirstResponder];
return NO;
}else{
return YES;
}
}
Apart from the fact that you do
else if (uname.text.length < 8)
{
UIAlertView *err4 = [[UIAlertView alloc]
initWithTitle:#"Invalid!" message:#"Enter a password longer than 8 chars." delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[err4 show];
NSLog(#"%#",uname.text);
}
instead of
else if (passw.text.length < 8)
{
UIAlertView *err4 = [[UIAlertView alloc]
initWithTitle:#"Invalid!" message:#"Enter a password longer than 8 chars." delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[err4 show];
NSLog(#"%#",passw.text);
}
Your code should work just fine.
Also, bear in mind that a text field's text won't be nil, it will just be an empty string (lenght == 0), unless you explicitly set it to nil.
I have about 4 alert views with different criteria when they appear. In all 4 views, the right button should always do the same thing.
I use the code below to try and say IF the buttonIndex == 1, do something.
Currently, It only works in one of my alert views. The others just end up closing the alert view and never running the code for IF buttonIndex == 1.
Any ideas would be appreciated.
if (a==1) {
NSString *message = [[NSString alloc] initWithFormat:
#"Only $%#!",dollas.text];
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:
#"Really?!"
message:message
delegate:self
cancelButtonTitle:#"Close"
otherButtonTitles:#"Facebook",nil];
[alert show];
[alert release];
[message release];
}
else if (a==2) {
NSString *message = [[NSString alloc] initWithFormat:
#"Somone just paid you $%#", dollas.text];
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:
#"Swish!"
message:message
delegate:nil
cancelButtonTitle:#"Close"
otherButtonTitles:#"Facebook",nil];
[alert show];
[alert release];
[message release];
}
And the delegate:
- (void)alertView:(UIAlertView *)actionSheet didDismissWithButtonIndex:(NSInteger)buttonIndex {
if (buttonIndex == 1)
{
do.stuff;
}
You should be setting the delegate to self so that method gets called.
IE -
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:
#"Really?!"
message:message
delegate:self //SELF
cancelButtonTitle:#"Close"
otherButtonTitles:#"Facebook",nil];
Set the tags on each alertview and inside -didDismissWithButtonIndex check first for the alerts tag
eg:
if (a==1) {
NSString *message = [[NSString alloc] initWithFormat:
#"Only $%#!",dollas.text];
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:
#"Really?!"
message:message
delegate:self
cancelButtonTitle:#"Close"
otherButtonTitles:#"Facebook",nil];
alert.tag = 1;
[alert show];
[alert release];
[message release];
}
else if (a==2) {
NSString *message = [[NSString alloc] initWithFormat:
#"Somone just paid you $%#", dollas.text];
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:
#"Swish!"
message:message
delegate:nil
cancelButtonTitle:#"Close"
otherButtonTitles:#"Facebook",nil];
alert.tag = 2;
[alert show];
[alert release];
[message release];
}
then in -didDismissWithButtonIndex
- (void)alertView:(UIAlertView *)actionSheet didDismissWithButtonIndex:(NSInteger)buttonIndex {
if (buttonIndex == 1 && actionSheet.tag == 1)
{
do.stuff;
}
else if (buttonIndex == 1 && actionSheet.tag == 2)
{
do.otherStuff;
}
For the case (a == 2) you set the UIAlertView delegate to nil, so - (void)alertView:(UIAlertView *)actionSheet didDismissWithButtonIndex:(NSInteger)buttonIndex shouldn't even be getting called for this case. Change it to set the delegate to self.
I'm trying to authenticate users through webserver and calling the following method in my login view button touch to do so.
- (void)connection:(NSURLConnection *)connection
didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
{
NSLog(#"challenge failure %d", [challenge previousFailureCount]);
// Access has failed two times...
if ([challenge previousFailureCount] == 0)
{
//NSLog(#"desc %#",[connection description]);
NSURLCredential *cred = [[[NSURLCredential alloc] initWithUser:userName.text password:passWord.text
persistence:NSURLCredentialPersistenceForSession] autorelease];
[[challenge sender] useCredential:cred forAuthenticationChallenge:challenge];
// need to call new view on correct authentication
[connection release];
}
else {// Answer the challenge
UIAlertView *alert = [[UIAlertView alloc]
initWithTitle:#"Authentication error"
message:#"Invalid Credentials" delegate:self
cancelButtonTitle:#"Retry"
otherButtonTitles:nil];
[alert show];
[alert release];
}
}
Is there any status returned after successful authentication so that i may use that status and pass on to next view else display the error.
Jsut Call connectionDidFinishLoading: Method.
Ok, as said in my comment:
Actually the connectionDidFinishLoading: method tells you that you can proceed to the next step.