I know, there are some threads about this, but I didn't really get what I excatly have to do for my class.
- (void)authenticateLocalUser {
if (!gameCenterAvailable) return;
NSLog(#"Authenticating local user...");
if ([GKLocalPlayer localPlayer].authenticated == NO) {
[[GKLocalPlayer localPlayer]
authenticateWithCompletionHandler:nil];
} else {
NSLog(#"Already authenticated!");
}
}
How can I fix the problem, it says "authenticateWithCompletionHandler" is deprecated.
Can somebody help me out?
EDIT:
With the new code
- (void)authenticateLocalUser {
if (!gameCenterAvailable) return;
NSLog(#"Authenticating local user...");
if ([GKLocalPlayer localPlayer].authenticated == NO) {
GKLocalPlayer *localPlayer = [GKLocalPlayer localPlayer];
[localPlayer setAuthenticateHandler:(^(UIViewController* viewcontroller, NSError *error) {
//[localPlayer authenticateWithCompletionHandler:^(NSError *error) { OLD CODE!
if(localPlayer.isAuthenticated) {
//do some stuff
}else {
// not logged in
}
})];
} else {
NSLog(#"Already authenticated!");
}
}
The error is : Capturing "localPlayer" strongly in this block is likely to lead to a retain circle . Its in the if(localPlayer.isAuthenticated).
also, the Game Center is not popping up anymore. If i want to let it pop up again with this code:
localPlayer.authenticateHandler = ^(UIViewController *viewController,NSError *error) {
if (localPlayer.authenticated) {
//already authenticated
} else if(viewController) {
[self presentViewController:viewController];//present the login form
} else {
//problem with authentication,probably bc the user doesn't use Game Center
}
};
Same Error here.
Here as img:
You replaced the method with this method:
-(void)authenticateLocalUser {
NSLog(#"Authenticating local user ...");
if(!gameCenterAvailable) {
return;
}
GKLocalPlayer *localPlayer = [GKLocalPlayer localPlayer];
localPlayer.authenticateHandler = ^(UIViewController *viewController, NSError *error){
NSLog(#"authenticateHandler");
if (viewController != nil)
{
NSLog(#"viewController != nil");
}
else if (localPlayer.isAuthenticated)
{
NSLog(#"localPlayer already authenticated");
//do some stuff
}
else
{
NSLog(#"local player not authenticated");
// not logged in
}
};
}
-(void)authenticateLocalUser {
if(!gameCenterAvailable) { return; }
GKLocalPlayer *localPlayer = [GKLocalPlayer localPlayer];
__weak GKLocalPlayer *blockLocalPlayer = localPlayer;
localPlayer.authenticateHandler = ^(UIViewController *viewController, NSError *error){
NSLog(#"authenticateHandler");
if (viewController != nil)
{
NSLog(#"viewController != nil");
}
else if (blockLocalPlayer.isAuthenticated)
{
NSLog(#"localPlayer already authenticated");
//do some stuff
}
else
{
NSLog(#"local player not authenticated");
// not logged in
}
};
}
this worked for me
Related
So I have an App that has a Game A and Game B.
I have Game Centre implemented correctly for Game A (I used the AppCoda tutorial like I have for every game so far).
Now I'm having troubles getting it to submit the score if Game B is played. I need to the score to be submitted to the second leaderboard created in iTunes Connect.
This is my part of my ViewController that authenticates the User and uses the identifier for the leaderboard etc.
ViewController.h:
-(void)authenticateLocalPlayer{
GKLocalPlayer *localPlayer = [GKLocalPlayer localPlayer];
localPlayer.authenticateHandler = ^(UIViewController *viewController, NSError *error){
if (viewController != nil) {
[self presentViewController:viewController animated:YES completion:nil];
}
else{
if ([GKLocalPlayer localPlayer].authenticated) {
_gameCenterEnabled = YES; //added bool indentier to .h
// Get the default leaderboard identifier.
[[GKLocalPlayer localPlayer] loadDefaultLeaderboardIdentifierWithCompletionHandler:^(NSString *leaderboardIdentifier, NSError *error) {
if (error != nil) {
NSLog(#"%#", [error localizedDescription]);
}
else{
_leaderboardIdentifier = leaderboardIdentifier; //added pointer to NSString to .h
}
}];
}
else{
_gameCenterEnabled = NO;
}
}
};
}
Seems my Game B View Controller is scorings/submitting just like Game A, I figured I could just change the above code to this:(to allow for the second identifier):
-(void)authenticateLocalPlayer{
GKLocalPlayer *localPlayer = [GKLocalPlayer localPlayer];
localPlayer.authenticateHandler = ^(UIViewController *viewController, NSError *error){
if (viewController != nil) {
[self presentViewController:viewController animated:YES completion:nil];
}
else{
if ([GKLocalPlayer localPlayer].authenticated) {
_gameCenterEnabled = YES; //added bool indentier to .h
_gameCenterEnabled2 = YES;
// Get the default leaderboard identifier.
[[GKLocalPlayer localPlayer] loadDefaultLeaderboardIdentifierWithCompletionHandler:^(NSString *leaderboardIdentifier, NSError *error) {
if (error != nil) {
NSLog(#"%#", [error localizedDescription]);
}
else{
_leaderboardIdentifier = leaderboardIdentifier; //added pointer to NSString to .h
}
}];
// Get the second leaderboard identifier.
[[GKLocalPlayer localPlayer] loadDefaultLeaderboardIdentifierWithCompletionHandler:^(NSString *leaderboardIdentifier2, NSError *error) {
if (error != nil) {
NSLog(#"%#", [error localizedDescription]);
}
else{
_leaderboardIdentifier2 = leaderboardIdentifier2; //added pointer to NSString to .h
}
}];
}
else{
_gameCenterEnabled = NO;
_gameCenterEnabled2 = NO;
}
}
};
}
But for some reason, it won't send the score to the second leaderboard and I can't find any resources/tutorials on how to submit a score to a "non-default" leaderboard...
Ok, so I re-read the Apple Doc and found the solution.
Obviously there can only be ONE default leaderboard (which is authenticated and set in the code in my question)... This doesn't need changing like I originally thought. (I forgot it was used to set the default board).
So what I needed to do was set the Leaderboard identifier to the second leaderboard's identifier (this will be whatever ID you used in iTunes Connect when making the second leaderboard).
I did it in the Game B View Controller when reporting a score like this:
-(void)reportScore{
//set identifier manually as it's not the default leaderboard
GKScore *score = [[GKScore alloc] initWithLeaderboardIdentifier:#"The_GameB_Leaderboard"];
//GKScore *score = [[GKScore alloc] initWithLeaderboardIdentifier:_leaderboardIdentifier2];
score.value = ScoreNumber_B; //Game B HighScore
[GKScore reportScores:#[score] withCompletionHandler:^(NSError *error) {
if (error != nil) {
NSLog(#"%#", [error localizedDescription]);
}
}];
NSLog(#"Reported to Game Center...");
}
There's no need to change the -(void)authenticateLocalPlayer method unless you need to add the GameB bool like I did, in which case you can add it below the GameA Bool:
if ([GKLocalPlayer localPlayer].authenticated) {
_gameCenterEnabled = YES; //added bool indentier to .h
_gameCenterEnabled2 = YES; //GameB bool
.
.
.
}
I hope this helps.
How can I display the GameCenter Login view again, if a user has canceled during first login attempt.
I am using the following code from raywenderlich tutorial,
-(void) authenticateLocalPlayer {
__weak GKLocalPlayer* localPlayer = [GKLocalPlayer localPlayer];
localPlayer.authenticateHandler = ^(UIViewController *viewController, NSError *error) {
[self setLastError:error];
if (localPlayer.authenticated) {
_gameCenterFeaturesEnabled = YES;
currentPlayer = localPlayer;
//NSLog(#"Authenticated User");
[self.delegate2 enableButtonOnAuthentication];
} else if(viewController) {
[self presentViewController:viewController];
} else {
_gameCenterFeaturesEnabled = NO;
}
};
}
The above method is called from viewDidLoad,
[[GameKitHelper sharedGameKitHelper] authenticateLocalPlayer];
[[GameKitHelper sharedGameKitHelper] setDelegate2:self];
If a user cancels the login, I am displaying a message, "to play you must login".
And if tries to call the above method again on a button click the block code does not seems to execute.
Can you point out to what am I doing wrong here?
I released my app yesterday and few units were sold so far. But I figured out that the leaderboard doesn't work properly. I can only see my own score when I'm done with my game. Is there some kind of delay until the leaderboard is updated or is it a problem on my implementation? I'd really appreciate if I have my implementation checked by someone who knows how to do it properly. On a side note, I already configured my leaderboard on itunes-connect and enabled it as well. I'm not sure if GKLocalPlayer's instance method loadDefaultLeaderboardIdentifierWithCompletionHandler:^(NSString *leaderboardIdentifier, NSError *error) is the right method to use to load the correct leaderboardID. Do I manually have to declare my leaderboard ID on Xcode somewhere with the leaderboardID I created on itunes-connect? because I find it odd that I never get to use it on the actual implementation... I want this error fixed as soon as possible and I need you guys' help. Thanks.
(void)authenticateLocalPlayer {
GKLocalPlayer *localPlayer = [GKLocalPlayer localPlayer];
localPlayer.authenticateHandler = ^(UIViewController *viewController, NSError *error){
if (viewController != nil) {
[self presentViewController:viewController animated:YES completion:nil];
}
else{
if ([GKLocalPlayer localPlayer].authenticated) {
_gameCenterEnabled = YES;
NSLog(#"authenticated");
// Get the default leaderboard identifier.
[[GKLocalPlayer localPlayer] loadDefaultLeaderboardIdentifierWithCompletionHandler:^(NSString *leaderboardIdentifier, NSError *error) {
if (error != nil) {
NSLog(#"here");
NSLog(#"%#", [error description]);
}
else{
_leaderboardIdentifier = leaderboardIdentifier;
}
}];
}
else{
_gameCenterEnabled = NO;
}
}
};
}
- (void)reportScore:(NSNotification *) notification {
if (_gameCenterEnabled) {
NSDictionary *userInfo = notification.userInfo;
NSNumber *score = [userInfo objectForKey:#"highestScore"];
GKScore *gkscore = [[GKScore alloc]initWithLeaderboardIdentifier:_leaderboardIdentifier];
gkscore.value = [score integerValue];
[GKScore reportScores:#[gkscore] withCompletionHandler:^(NSError *error) {
if (error != nil) {
NSLog(#"%#", [error localizedDescription]);
}
}];
}
}
- (void)showLeaderboard{
GKGameCenterViewController *gcViewController = [[GKGameCenterViewController alloc] init];
gcViewController.gameCenterDelegate = self;
gcViewController.viewState = GKGameCenterViewControllerStateLeaderboards;
gcViewController.leaderboardIdentifier = _leaderboardIdentifier;
[self presentViewController:gcViewController animated:YES completion:nil];
}
- (void)gameCenterViewControllerDidFinish:(GKGameCenterViewController *)gameCenterViewController
{
[gameCenterViewController dismissViewControllerAnimated:YES completion:nil];
}
I'm getting the error instance method '-callDelegateOnMainThread:withArg:error:' not found what should I do here? I merged in some achievement code into Ray Wenderlichs turn based multiplayer code.
The functions are part of the following interface:
#interface GCTurnBasedMatchHelper : NSObject <GKTurnBasedMatchmakerViewControllerDelegate, GKTurnBasedEventHandlerDelegate, GKAchievementViewControllerDelegate, MFMessageComposeViewControllerDelegate> {
BOOL gameCenterAvailable;
BOOL userAuthenticated;
UIViewController *presentingViewController;
NSMutableDictionary* earnedAchievementCache;
GKTurnBasedMatch *currentMatch;
//id <GCTurnBasedMatchHelperDelegate> delegate;
}
Here are the functions
- (void) submitAchievement: (NSString*) identifier percentComplete: (double) percentComplete
{
//GameCenter check for duplicate achievements when the achievement is submitted, but if you only want to report
// new achievements to the user, then you need to check if it's been earned
// before you submit. Otherwise you'll end up with a race condition between loadAchievementsWithCompletionHandler
// and reportAchievementWithCompletionHandler. To avoid this, we fetch the current achievement list once,
// then cache it and keep it updated with any new achievements.
if(self.earnedAchievementCache == NULL)
{
[GKAchievement loadAchievementsWithCompletionHandler: ^(NSArray *scores, NSError *error)
{
if(error == NULL)
{
NSMutableDictionary* tempCache = [NSMutableDictionary dictionaryWithCapacity: [scores count]];
for (GKAchievement* score in scores)
{
[tempCache setObject: score forKey: score.identifier];
}
self.earnedAchievementCache = tempCache;
[self submitAchievement: identifier percentComplete: percentComplete];
}
else
{
//Something broke loading the achievement list. Error out, and we'll try again the next time achievements submit.
[self callDelegateOnMainThread: #selector(achievementSubmitted:error:) withArg: NULL error: error];
}
}];
}
else
{
//Search the list for the ID we're using...
GKAchievement* achievement = [self.earnedAchievementCache objectForKey: identifier];
if(achievement != NULL)
{
if((achievement.percentComplete >= 100.0) || (achievement.percentComplete >= percentComplete))
{
//Achievement has already been earned so we're done.
achievement= NULL;
}
achievement.percentComplete= percentComplete;
}
else
{
achievement = [[[GKAchievement alloc] initWithIdentifier: identifier] autorelease];
achievement.percentComplete= percentComplete;
//Add achievement to achievement cache...
[self.earnedAchievementCache setObject: achievement forKey: achievement.identifier];
}
if(achievement != NULL)
{
//Submit the Achievement...
[achievement reportAchievementWithCompletionHandler: ^(NSError *error)
{
[self callDelegateOnMainThread: #selector(achievementSubmitted:error:) withArg: achievement error: error];
}];
}
}
}
- (void) achievementSubmitted: (GKAchievement*) ach error:(NSError*) error;
{
if((error == NULL) && (ach != NULL))
{
if (ach.percentComplete == 100.0) {
//UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Achievement Earned!"
// message:(#"%#",ach.identifier)
// delegate:nil
// cancelButtonTitle:#"OK"
// otherButtonTitles:nil];
//[alert show];
//[alert release];
TRACE("achievement submitted %s\n", [ach.identifier UTF8String]);
}
}
else
{
// Achievement Submission Failed.
printf("Achievement Submission Failed\n");
}
}
Looks like this is the missing code you are looking for. Just put it inside of your GCTurnBasedMatchHelper.m file.
- (void) callDelegateOnMainThread: (SEL) selector withArg: (id) arg error: (NSError*) err
{
dispatch_async(dispatch_get_main_queue(), ^(void)
{
[self callDelegate: selector withArg: arg error: err];
});
}
- (void) callDelegate: (SEL) selector withArg: (id) arg error: (NSError*) err
{
assert([NSThread isMainThread]);
if([delegate respondsToSelector: selector])
{
if(arg != NULL)
{
[delegate performSelector: selector withObject: arg withObject: err];
}
else
{
[delegate performSelector: selector withObject: err];
}
}
else
{
NSLog(#"Missed Method");
}
}
Just add the method which doesn't exist.
iOS 6 provides new view controller to display Game Center info: GKGameCenterViewController.
Does iOS 5.1 provide something similar (except separate controllers for Leader-board and Achievements)?
Here's how I handle both the old and new GameCenter APIs in my app which supports everything between iOS 4 and 7.
I started with the GameCenterManager.m sample code.
1) In GameCenterManager.m, I changed authenticateLocalUser
- (void) authenticateLocalUser
{
if([GKLocalPlayer localPlayer].authenticated == NO)
{
if ([[GKLocalPlayer localPlayer] respondsToSelector: #selector(setAuthenticateHandler:)]) {
[[GKLocalPlayer localPlayer] setAuthenticateHandler:(^(UIViewController* viewcontroller, NSError *error) {
[self callDelegateOnMainThread: #selector(processGameCenterAuth:error:) withArg: viewcontroller error: error];
})];
} else {
[[GKLocalPlayer localPlayer] authenticateWithCompletionHandler:^(NSError *error)
{
[self callDelegateOnMainThread: #selector(processOldGameCenterAuth:) error: error];
}];
}
}
}
2) And then in my main view controller, here are my two versions of the authentication handler.
- (void) processOldGameCenterAuth: (NSError*) error; {
// for iOS < 6.0 without the viewcontroller parameter
GKLocalPlayer *localPlayer = [GKLocalPlayer localPlayer];
if (localPlayer.isAuthenticated) {
usingGamecenter = TRUE;
[self.gameCenterManager reloadHighScoresForCategory: kOverallLeaderboard];
} else {
usingGamecenter = FALSE;
}
// *** this is where you update your UI after game center login
}
- (void) processGameCenterAuth: (UIViewController*) gameCenterController error: (NSError*) error; {
if (gameCenterController) {
[self presentViewController:gameCenterController animated:YES completion:nil];
} else {
[self processOldGameCenterAuth: error];
}
}