How to share saved games across devices?












6















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?










share|improve this question





























    6















    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?










    share|improve this question



























      6












      6








      6








      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?










      share|improve this question
















      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






      share|improve this question















      share|improve this question













      share|improve this question




      share|improve this question








      edited Oct 25 '18 at 10:13







      modiX

















      asked Oct 24 '18 at 17:42









      modiXmodiX

      2,09422848




      2,09422848
























          1 Answer
          1






          active

          oldest

          votes


















          1














          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.






          share|improve this answer























            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
            });


            }
            });














            draft saved

            draft discarded


















            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









            1














            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.






            share|improve this answer




























              1














              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.






              share|improve this answer


























                1












                1








                1







                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.






                share|improve this answer













                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.







                share|improve this answer












                share|improve this answer



                share|improve this answer










                answered Nov 18 '18 at 12:14









                modiXmodiX

                2,09422848




                2,09422848






























                    draft saved

                    draft discarded




















































                    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.




                    draft saved


                    draft discarded














                    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





















































                    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







                    Popular posts from this blog

                    Guess what letter conforming each word

                    Port of Spain

                    Run scheduled task as local user group (not BUILTIN)