Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

GKLocalPlayer Auth Crash on iOS6 with Modal Transition Error

My app keeps a record of whether it's successfully authenticated with Game Center. When it starts a new game or when the user looks at the score list, if a local player hasn't been successfully authenticated, and if the app isn't in the middle of trying to authenticate a user at the time, it tries again.

(Why? In case you've moved from a no-network zone to a network zone.)

Unfortunately, under iOS6/XCode 4.5, it's started crashing. Or at least it seems to in certain limited circumstances: when a user fails to log in with a bad password and/or an account that doesn't exist. On a successful login, all is well.

After that unsuccessful login, when I go and do something that causes a reauth check to occur I get this:

2012-09-25 15:54:47.829 APP NAME [1493:907] * Assertion failure in -[UIWindowController transition:fromViewController:toViewController:target:didEndSelector:], /SourceCache/UIKit/UIKit-2372/UIWindowController.m:211

Then this actually crashes:

2012-09-25 15:55:25.569 APP NAME [1493:907] * Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Attempting to begin a modal transition from <GKModalRootViewController: 0x1cd8b2a0> to <GKHostedAuthenticateViewController: 0x1e31a350> while a transition is already in progress. Wait for viewDidAppear/viewDidDisappear to know the current transition has completed' * First throw call stack: (0x394932a3 0x31db297f 0x3949315d 0x383fd2af 0x3640377b 0x36402fcf 0x394969c4 0x393edfeb 0x36521733 0x32a83d2d 0x3264b11f 0x3264a4b7 0x3264f1bd 0x39466f3b 0x393d9ebd 0x393d9d49 0x353132eb 0x3636b301 0x7e863 0x7e808) libc++abi.dylib: terminate called throwing an exception

This is the troublesome code:

-(void)authenticateLocalUser {

    if (!self.checkingLocalPlayer) {
        self.checkingLocalPlayer = YES;
        GKLocalPlayer *thisPlayer = [GKLocalPlayer localPlayer];

        if (!thisPlayer.authenticated) {

            [[GKLocalPlayer localPlayer]
             authenticateWithCompletionHandler:^(NSError *error)
             {
                 [self finishGameCenterAuthWithError:error];
             }
             ];
        }
    }
}

Any idea what I'm doing wrong here?

like image 932
Shannon A. Avatar asked Sep 25 '12 23:09

Shannon A.


2 Answers

Ggrrrhhh same issue, think I found it...ios6 have deprecated authenticateWithCompletionHandler see link, the propose you use AuthenticateHandler.

http://developer.apple.com/library/IOS/#documentation/GameKit/Reference/GKLocalPlayer_Ref/Reference/Reference.html#//apple_ref/occ/instp/GKLocalPlayer/authenticateHandler

This appears to work...

GKLocalPlayer *localPlayer = [GKLocalPlayer localPlayer];
[localPlayer setAuthenticateHandler:(^(UIViewController* viewcontroller, NSError *error) {

//[localPlayer authenticateWithCompletionHandler:^(NSError *error) { OLD CODE!
    if (localPlayer.isAuthenticated)
    {
        //do some stuff
    }
    else {

        UIAlertView *alertView = [[UIAlertView alloc]
                                  initWithTitle:@"NOT AUTHORISED"
                                  message:@"YOUR'RE NOT LOGGED INTO GC."
                                  delegate:self
                                  cancelButtonTitle:@"OK"
                                  otherButtonTitles:nil];
        [alertView show];

    }
})];
like image 173
Jamie West Avatar answered Sep 24 '22 21:09

Jamie West


Jamie West correctly noted that the problem was the changeover to authenticateHandler in iOS6, which seems to have left authenticateWithCompletionHandler: broken. Since it's been deprecated, Apple is unlikely to fix their bug.

Here's everything that I needed. Note both the conditional to check iOS5 version (to support both 5 and 6) and the need to call up a view controller if it's sent (e.g., authenticateHandler: isn't a direct replacement to authenticateWithCompletionHandler: — you have to do some extra work).

-(void)authenticateLocalUser {

    if (!self.checkingLocalPlayer) {
        self.checkingLocalPlayer = YES;
        GKLocalPlayer *thisPlayer = [GKLocalPlayer localPlayer];

        if (!thisPlayer.authenticated) {

#define SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(v)  ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedAscending)

            if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"6.0")) {

                [thisPlayer setAuthenticateHandler:(^(UIViewController* viewcontroller, NSError *error) {

                    if (viewcontroller) {
                        [self.delegate presentViewController:viewcontroller];
                    } else {
                        [self finishGameCenterAuthWithError:error];
                    }

                })];

            } else {

                [[GKLocalPlayer localPlayer]
                 authenticateWithCompletionHandler:^(NSError *error)
                 {
                     [self finishGameCenterAuthWithError:error];
                 }
                 ];
            }

        }
    }
}

The presentViewController: function simply calls up the new view controller from my top view controller (which I had to figure out by hand in my program to make all this work):

-(void)presentViewController:(UIViewController *)thisVC {

    [myTopVC presentModalViewController:thisVC animated:YES];

}

Unfortunately, as of iOS6, it looks like GameCenter won't try to reauth a user if that's already been canceled in the current session, so at least some of my point of reauthing when appropriate is gone. But, it's still there for iOS5 (and maybe other situations than canceling?).

It will try to reauth when the app is called back up from sleep if a user has gone and done something else.

like image 42
Shannon A. Avatar answered Sep 23 '22 21:09

Shannon A.