I implemented GameKit into my iOS game including the saved game feature.
Here an example how I save and load a game:
MobSvcSavedGameData.h
#ifndef MOBSVC_SAVEDGAMEDATA_H
#define MOBSVC_SAVEDGAMEDATA_H
#import
@interface MobSvcSavedGameData : NSObject
@property (readwrite, retain) NSString *data;
+(instancetype)sharedGameData;
-(void)reset;
@end
#endif /* MOBSVC_SAVEDGAMEDATA_H */
MobSvcSavedGameData.m
#import "MobSvcSavedGameData.h"
#import
@interface MobSvcSavedGameData ()
@end
@implementation MobSvcSavedGameData
#pragma mark MobSvcSavedGameData implementation
static NSString * const sgDataKey = @"data";
+ (instancetype)sharedGameData {
static id sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}
- (void)reset
{
self.data = nil;
}
- (void)encodeWithCoder:(NSCoder *)encoder
{
[encoder encodeObject:self.data forKey: sgDataKey];
}
- (nullable instancetype)initWithCoder:(nonnull NSCoder *)decoder {
self = [self init];
if (self) {
self.data = [decoder decodeObjectForKey:sgDataKey];
}
return self;
}
@end
For simplicity my saved game object above has only a `NSString` which will be serialised and uploaded like so:
void MobSvc::uploadSavedGameDataAwait(const char *name, const char *data)
{
GKLocalPlayer *mobSvcAccount = [GKLocalPlayer localPlayer];
if(mobSvcAccount.isAuthenticated)
{
MobSvcSavedGameData *savedGameData = [[MobSvcSavedGameData alloc] init];
savedGameData.data = [NSString stringWithUTF8String:data];
[mobSvcAccount saveGameData:[NSKeyedArchiver archivedDataWithRootObject:savedGameData] withName:[[NSString alloc] initWithUTF8String:name] completionHandler:^(GKSavedGame * _Nullable savedGame __unused, NSError * _Nullable error) {
if(error == nil)
{
NSLog(@"Successfully uploaded saved game data");
}
else
{
NSLog(@"Failed to upload saved game data: %@", error.description);
}
}];
}
}
And this is how I download the most recent saved game on the next play session again:
void MobSvc::downloadSavedGameDataAwait(const char *name)
{
GKLocalPlayer *mobSvcAccount = [GKLocalPlayer localPlayer];
if(mobSvcAccount.isAuthenticated)
{
[mobSvcAccount fetchSavedGamesWithCompletionHandler:^(NSArray * _Nullable savedGames, NSError * _Nullable error) {
if(error == nil)
{
GKSavedGame *savedGameToLoad = nil;
for(GKSavedGame *savedGame in savedGames) {
const char *sname = savedGame.name.UTF8String;
if(std::strcmp(sname, name) == 0)
{
if (savedGameToLoad == nil || savedGameToLoad.modificationDate < savedGame.modificationDate) {
savedGameToLoad = savedGame;
}
}
}
if(savedGameToLoad != nil) {
[savedGameToLoad loadDataWithCompletionHandler:^(NSData * _Nullable data, NSError * _Nullable error) {
if(error == nil)
{
MobSvcSavedGameData *savedGameData = [NSKeyedUnarchiver unarchiveObjectWithData:data];
NSLog(@"Successfully downloaded saved game data: %@", [savedGameData.data cStringUsingEncoding:NSUTF8StringEncoding]);
}
else
{
NSLog(@"Failed to download saved game data: %@", error.description);
}
}];
}
}
else
{
NSLog(@"Failed to prepare saved game data: %@", error.description);
}
}];
}
}
I tested this by uploading a random string and receiving it on the next session by using the same `name`. It works! However, as soon as I try to download the saved game from my second iPhone it does not work. On both phones I'm logged into the same Game-Center account, I could confirm this by comparing the `playerId` in the `GKLocalPlayer` instance.
I've set up the proper iCloud container and linked my game to it, but the logs in the iCloud container backend remain empty.
What is going on? How can I share the saved game across Apple devices?