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).
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.
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.