简体   繁体   中英

iOS programmatically triggered segue is performed multiple times on success

I connect my app to a rest API to log in the user. When the log in procedure is finished, I use the NSNotificationCenter to inform the controller of the successful log in and for the app to perform the segue to take the user ot the main menu. Here's my code:

//perform log in, or show error message
-(void)logInAttemptComplete:(NSNotification *)notification
{
Boolean loginSuccessful = (Boolean)[[notification userInfo] objectForKey:@"loginSuccessful"];

[SVProgressHUD dismiss];
//if the suer was successfully logged in, take him to the main menu
if(loginSuccessful)
{
    //go to the main menu
    [self performSegueWithIdentifier:@"logInSuccessfulSegue" sender:self];
}
else
{
    //copy error code and display appropriate error
    int errorCode = [[[notification userInfo] objectForKey:@"HTTP Message"] intValue];
    //404 no server, 401 wrong password/no user
    if(errorCode==401)
    {
        //create and show error alert view
        UIAlertView *loginErrorAlertView = [[UIAlertView alloc] initWithTitle:@"Log In Failure" message:@"Wrong credentials. Check your Username and/or Password." delegate:self cancelButtonTitle:@"OK" otherButtonTitles: nil];
        [loginErrorAlertView show];
    }
    else
    {
        //create and show error alert view
        UIAlertView *loginErrorAlertView = [[UIAlertView alloc] initWithTitle:@"Log In Failure" message:@"Failed to contact server, please try again later." delegate:self cancelButtonTitle:@"OK" otherButtonTitles: nil];
        [loginErrorAlertView show];
    }
}
}

The problem is that if the log in isn't successful on the first try, and let's say it take X number of tries to successfully log in, then the segue is performed X number of times. It does end up on the main menu of course, but the end result is ugly. Any ideas how to fix this? Perhaps I should avoid using a segue and programmatically take hte user directly to the other controller?

EDIT WITH ANSWER

So I made an error by adding the line

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(logInAttemptComplete:) name:@"logInNotification" object:nil];

inside the function of pressing the log in button.

I fixed it by removing the observer after the login attempt has failed

if(loginSuccessful)
{
//go to the main menu
[self performSegueWithIdentifier:@"logInSuccessfulSegue" sender:self];
}
else
{

[[NSNotificationCenter defaultCenter] removeObserver:self name:@"logInNotification" object:nil];

I would use two different notifications, this code seems more complicated than it should be.

You did not provide your the login code itself, so I can only answer in general. Create two notifications (one for successful and one for login failure). The login controller sends either notification depending on the result of the login attempt.

You split the above method in two methods which are called when either of the notifications is sent.

Your code might look like this:

[manager GET:stringWithURLforRequest parameters:nil success:^(AFHTTPRequestOperation *task, id responseObject) {

   // ...
   [[NSNotificationCenter defaultCenter] postNotificationName:@"LogInSuccessful" object:nil userInfo:nil];

} failure:^(AFHTTPRequestOperation *task, NSError *error) {

   // ...    
   [[NSNotificationCenter defaultCenter] postNotificationName:@"LogInFailure" object:nil userInfo:@{ @"HTTP Message":statusCodeNumber}];

}];

... and you just need to add another notification listening to your other view controller. If you do it like that, you have a safer differentiation between login failure and success (better semantics).

If @"loginSuccessful" in your userInfo is NSNumber use:

BOOL loginSuccessful = [notification.userInfor[@"loginSuccessful"] booleanValue];

In the original code if object was not nil (and if I understand correctly it always was) you would always get loginSuccessful with value YES , because you checked an object existence not it's value.

You use NSNumber in this line:

@{@"loginSuccessful":@YES}

@YES is just short for [NSNumber numberWithBool:YES]

Acording to this header Boolean is just unsigned char so you're signing pointer to this char and it will always be true (not NULL).

From what I understand you're sending a notification on success, and it gets called too many times if there were retries.

A one-liner fix would be to STOP listening to that notification in the success method.

So you'd have something along these lines (pseudo code)

- (void)loginAttempt:(NSNotifcation*)notif{
    if (success){
//Stop listening to that notification, so the method never gets called again
    performSegueWithIndetifier
    }else{

    //retry

    }
}

Another dirty one-liner way to do it is to keep an ivar of the number of tries and only call the perform segue if that number is currently equal to zero.

I think my solution will fix your problem, but if you have the time I suggest you review your design in a way that you segue is only called when it needs to be called or that the callbacks of the other X successes are ignored. A simple way to imagine that is to not retry until the previous login has given is result. And so you don't retry until you actually know if you have failed or not (wether it's a timeout or a success or anything else)

Found it:

the problem was that the controller kept "listening" even when there was a failed message being sent because of the

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(logInAttemptComplete:) name:@"logInNotification" object:nil];

being inside the function of pressing the log in button.

All I had to do was add the

[[NSNotificationCenter defaultCenter] removeObserver:self name:@"logInNotification" object:nil];

line after the

if(loginSuccessful)
{
//go to the main menu
[self performSegueWithIdentifier:@"logInSuccessfulSegue" sender:self];
}
else
{

else check.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM