How to share saved games across devices?
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 <Foundation/Foundation.h>
@interface MobSvcSavedGameData : NSObject <NSCoding>
@property (readwrite, retain) NSString *data;
+(instancetype)sharedGameData;
-(void)reset;
@end
#endif /* MOBSVC_SAVEDGAMEDATA_H */
MobSvcSavedGameData.m
#import "MobSvcSavedGameData.h"
#import <Foundation/Foundation.h>
@interface MobSvcSavedGameData () <NSObject, NSCoding>
@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<GKSavedGame *> * _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?
objective-c icloud game-center objective-c++ gamekit
add a comment |
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 <Foundation/Foundation.h>
@interface MobSvcSavedGameData : NSObject <NSCoding>
@property (readwrite, retain) NSString *data;
+(instancetype)sharedGameData;
-(void)reset;
@end
#endif /* MOBSVC_SAVEDGAMEDATA_H */
MobSvcSavedGameData.m
#import "MobSvcSavedGameData.h"
#import <Foundation/Foundation.h>
@interface MobSvcSavedGameData () <NSObject, NSCoding>
@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<GKSavedGame *> * _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?
objective-c icloud game-center objective-c++ gamekit
add a comment |
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 <Foundation/Foundation.h>
@interface MobSvcSavedGameData : NSObject <NSCoding>
@property (readwrite, retain) NSString *data;
+(instancetype)sharedGameData;
-(void)reset;
@end
#endif /* MOBSVC_SAVEDGAMEDATA_H */
MobSvcSavedGameData.m
#import "MobSvcSavedGameData.h"
#import <Foundation/Foundation.h>
@interface MobSvcSavedGameData () <NSObject, NSCoding>
@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<GKSavedGame *> * _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?
objective-c icloud game-center objective-c++ gamekit
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 <Foundation/Foundation.h>
@interface MobSvcSavedGameData : NSObject <NSCoding>
@property (readwrite, retain) NSString *data;
+(instancetype)sharedGameData;
-(void)reset;
@end
#endif /* MOBSVC_SAVEDGAMEDATA_H */
MobSvcSavedGameData.m
#import "MobSvcSavedGameData.h"
#import <Foundation/Foundation.h>
@interface MobSvcSavedGameData () <NSObject, NSCoding>
@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<GKSavedGame *> * _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?
objective-c icloud game-center objective-c++ gamekit
objective-c icloud game-center objective-c++ gamekit
edited Oct 25 '18 at 10:13
modiX
asked Oct 24 '18 at 17:42
modiXmodiX
2,09422848
2,09422848
add a comment |
add a comment |
1 Answer
1
active
oldest
votes
The above sample in the question works just fine. It's mandatory that the user logs into iCloud and uses the same apple ID on the Game Center login, because the saved games will be stored in the iCloud.
Unfortunately, I was testing the whole without iCloud, so it couldn't work.
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f52975069%2fhow-to-share-saved-games-across-devices%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
The above sample in the question works just fine. It's mandatory that the user logs into iCloud and uses the same apple ID on the Game Center login, because the saved games will be stored in the iCloud.
Unfortunately, I was testing the whole without iCloud, so it couldn't work.
add a comment |
The above sample in the question works just fine. It's mandatory that the user logs into iCloud and uses the same apple ID on the Game Center login, because the saved games will be stored in the iCloud.
Unfortunately, I was testing the whole without iCloud, so it couldn't work.
add a comment |
The above sample in the question works just fine. It's mandatory that the user logs into iCloud and uses the same apple ID on the Game Center login, because the saved games will be stored in the iCloud.
Unfortunately, I was testing the whole without iCloud, so it couldn't work.
The above sample in the question works just fine. It's mandatory that the user logs into iCloud and uses the same apple ID on the Game Center login, because the saved games will be stored in the iCloud.
Unfortunately, I was testing the whole without iCloud, so it couldn't work.
answered Nov 18 '18 at 12:14
modiXmodiX
2,09422848
2,09422848
add a comment |
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f52975069%2fhow-to-share-saved-games-across-devices%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown