Trying to figure out best way to work with Achievements in Game Center in case of offline mode (e.g. Airplane mode turned on).
As far as I understand Game Center in iOS 5+ takes care of offline submitted achievements and scores. It acts like a proxy cache and submits them to online Game Center next time user is online. Considering this here is what I do:
At the user authentication success I load achievments and store them in a dictionary.
[GKAchievement loadAchievementsWithCompletionHandler:^(NSArray *gcAchievments, NSError *error)
{
if (error) { ..skipped ..}
//This dictionary will store current achievments, so that we didn't submit them
//once more and didn't show notification.
achievments = [[NSMutableDictionary alloc] initWithCapacity:gcAchievments.count];
//Storing achievments in dictionary
for(GKAchievement *a in gcAchievments)
[achievments setObject:a forKey:a.identifier];
}];
later when I submit new achievement I check for achievement in a dictionary and don't submit if achievement is already completed. If I submit achievement I also add it to achievments
dictionary (in-memory) to immediatly reflrect that this achievment is already submitted.
GKAchievement *cachedAchievment = [achievments objectForKey:identifier];
if (cachedAchievment && cachedAchievment.percentComplete >= 100)
{
//Already unlocked this achievment.
return;
}
GKAchievement *achievement = [[GKAchievement alloc] initWithIdentifier: identifier];
if (achievement)
{
achievement.percentComplete = percent;
[achievement reportAchievementWithCompletionHandler:^(NSError *error)
{
if (!error)
{
//Flagging achievment as completed locally to avoid achieving it second time and showing notification.
[achievments setObject:achievement forKey:achievement.identifier];
//Now shoing notification banner.
GKAchievementDescription * desc = [achievmentsDescriptions objectForKey:achievement.identifier];
[[GKAchievementHandler defaultHandler] notifyAchievement:desc];
}
else
{
NSLog(@"Error in reporting achievements: %@", error);
}
}];
}
this approach allows me to trigger submission in the game code witout worying that I'll submit achievment twice or show notification banner twice.
Everyting works fine when Game Center is online. But in case I'm offline I have 2 issues.
Loading code returns error and initial dictionary of achievments
is not populated with already completed achievements, this means each time user launches the game he/she gets notification banner for each achievement again, even if it is already awarded. Then after it is placed into dictionary it is not displayed, but I don't want to show achievements even once for each game start.
I'm not sure if achievements submited offline will get to Game Center when user gets online. I can solve issue #1 using persistent storage (like database) to store achievement status between game launches, but should I resubmit them when Game Center is online and how do I detect if Game Center is actually online, since even offline reportAchievementWithCompletionHandler
completes without errors, so I cannot detect if it wasn't submitted. Here is how I'm testing, I've turn Airplane mode on, unlock few achievments, and then turn Airplane mode off, achievments don't get to Game Center, although this might be "sandbox" issue.
Add the persistent storage for "achievement unlocked" you mention, and also store a "successfully submitted to Game Center" flag for each achievement. Then, when online, check the results from loadAchievementsWithCompletionHandler
at startup and periodically to see if each achievement that is unlocked && !submitted
has actually reached Game Center and resubmit if not.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With