26

When using the authenticateHandler in iOS 6, game center won't present the login view if the user cancels it. I realize game center will auto lockout an app after 3 cancel attempts, but I'm talking about just 2 attempts. If they cancel the login, they have to leave the app and come back before game center will present the login even through the authenticateHandler is getting set again. Any ideas on how to handle this case in iOS 6?

It works fine when using the older authenticateWithCompletionHandler method:

#if __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_6_0
    GKLocalPlayer.localPlayer.authenticateHandler = authenticateLocalPlayerCompleteExtended;
#else
    [[GKLocalPlayer localPlayer] authenticateWithCompletionHandler:authenticateLocalPlayerComplete];
#endif

The reason this is important for my app is that it requires Game Center for multi-player. The app tries to authenticate to game center on launch, but if the user cancels we don't ask them at launch again so they won't get nagged. What we do is show a Game Center Login button if they aren't logged in when they select multi-player. The login button forces a game center login by calling authenticateWithCompletionHandler (and now by setting GKLocalPlayer.localPlayer.authenticateHandler again).

Tod Cunningham
  • 3,691
  • 4
  • 30
  • 32
  • 1
    It sounds like you are doing this already, but calling [[GKLocalPlayer localPlayer] authenticateWithCompletionHandler:nil] will cause the authenticateHandler to be called again with an authentication view controller. This method is deprecated though in iOS6. – Greg Oct 17 '12 at 16:43
  • I am using the depreciated call to get this to work, but I'm looking for the "right" way to do this without using deprecated calls. I tried setting the new GKLocalPlayer.localPlayer.authenticateHandler to nil and then back to my handler to see if that would work, and got an exception trying to set it to nil. I didn't try setting it to a different handler to see if that would trigger a login (that just seemed really hacky) – Tod Cunningham Oct 18 '12 at 00:45
  • I've tried switching the handler to another handler, and that also doesn't trigger a new login dialog opening. I posted on the developer forums to see if anyone has any advice, and will post back here if I hear anything. https://devforums.apple.com/message/744983 – Greg Oct 18 '12 at 03:17
  • 1
    Any updates on this? I have a "loading" screen where they must authenticate so I just want a button there to pull it up again. For now I'll just us an ivar and alert them. – Myles Best Jan 02 '13 at 21:08
  • 1
    Still no updates. It sounds like most people use the depreciated call in order to work around the issue. – Tod Cunningham Mar 26 '13 at 14:09
  • @TodCunningham do you solve this? even 1.5 years after your post I face same problem – Dima Deplov Mar 27 '14 at 23:36
  • Nope, it's still an issue. – Tod Cunningham Feb 24 '16 at 18:34

2 Answers2

2

Better use runtime checks (instancesRespondToSelector:) instead of preprocessor #if statements, so that you can use recommended methods where they are available and depreciated ones elsewhere. I actually found I need to distinguish three cases before setting the invite handler, as the authentication handler might also get called with a nil view controller:

 -(void)authenticateLocalPlayer
 {
     if ([[GKLocalPlayer class] instancesRespondToSelector:@selector(setAuthenticateHandler:)]) {
         [[GKLocalPlayer localPlayer] setAuthenticateHandler:^(UIViewController *gameCenterLoginViewController, NSError *error) {
             if (gameCenterLoginViewController) {
                 [self.presentedViewController presentViewController:gameCenterLoginViewController
                                                            animated:YES
                                                          completion:^{
                                                              [self setInviteHandlerIfAuthenticated];
                                                          }];
             } else {
                 [self setInviteHandlerIfAuthenticated];
             }
         }];
     } else { // alternative for iOS < 6
         [[GKLocalPlayer localPlayer] authenticateWithCompletionHandler:^(NSError *error) {
             [self setInviteHandlerIfAuthenticated];
         }];
     }
 }

Yet more cases must be distinguished within the invite handler, as matchForInvite:: is new in iOS6 as well and avoids yet another round through game center view controllers:

-(void)setInviteHandlerIfAuthenticated
{
    if ([GKLocalPlayer localPlayer].isAuthenticated) {
        [GKMatchmaker sharedMatchmaker].inviteHandler = ^(GKInvite *acceptedInvite, NSArray *playersToInvite) {
            if (acceptedInvite) {
                if ([GKMatchmaker instancesRespondToSelector:@selector(matchForInvite:completionHandler:)]) {
                    [self showInfoAnimating:YES completion:NULL];
                    [[GKMatchmaker sharedMatchmaker] matchForInvite:acceptedInvite
                                                  completionHandler:^(GKMatch *match, NSError *error) {
                                                      // ... handle invited match
                                                  }];
                } else {
                    // alternative for iOS < 6
                    GKMatchmakerViewController *mmvc = [[[GKMatchmakerViewController alloc] initWithInvite:acceptedInvite] autorelease];
                    mmvc.matchmakerDelegate = self;
                    // ... present mmvc appropriately
                    // ... handle invited match found in delegate method matchmakerViewController:didFindMatch:
                 }
            } else if (playersToInvite) {
                 // ... handle match initiated through game center
            }
        };
    }
}

Let me know if this helps.

pommy
  • 3,717
  • 1
  • 15
  • 25
  • 1
    The preprocessor macros were just for debugging and showing the problem. The problem I'm having is you can't trigger a login using the iOS 6 public API if the user cancels the initial login shown by Game Center. The pre-ios6 API supports this behavior be resetting the handler. – Tod Cunningham Aug 20 '13 at 04:07
0

I dont' think this is possible in iOS 6.0. There were API calls to do this in the early SDK builds that were removed before release.

In the WWDC 2012 Video: Session 516 - Integrating Your Games with Game Center [8:30] They actually show code where you call an authenticate method:

GKLocalPlayer *localPlayer = [GKLocalPlayer localPlayer];
localPlayer.authenticationHandler = //handle the callback...
[localPlayer authenticate];

This method is now private API but you can see it in action by calling:

[[GKLocalPlayer localPlayer] performSelector:@selector(_authenticate)];

It does exactly what you want, but can't be used because it's now private.


You can also trigger the authentication process by posting the UIApplicationWillEnterForegroundNotification notification:

[[NSNotificationCenter defaultCenter] postNotificationName:UIApplicationWillEnterForegroundNotification object:[UIApplication sharedApplication]];

I assume it would be unadvisable to do this in live code.

Rory O'Bryan
  • 1,894
  • 14
  • 22