iosswiftgamekit

GKSavedGame is not saved or loading back


I'm trying to save player game data using GKSavedGame from GameKit, but it doesn't work, and I don't get any error. (Is the data not saving? Is the data not loading back?)

One of the problem is that GKSavedGame is not well documented (there are no examples like we can find for other things), so we don't really know how to implement it correctly (e.g. what are the best practices?)

I'm using a simple struct GameData: Codable to store the game data, that I encode using JSONEncoder/JSONDecoder. Here is my GameService class with the part that doesn't work:

class GameService {
    
    // Shared instance
    
    static let shared = GameService()
    
    // Properties
    
    private(set) var isGameLoaded = false
    private(set) var gameData: GameData?
    
    // Methods
    
    func loadSavedGame(completionHandler: @escaping () -> Void) {
        // Check game is not loaded yet
        if isGameLoaded {
            completionHandler()
            return
        }
        
        // Get player
        let localPlayer = GKLocalPlayer.local
        if localPlayer.isAuthenticated {
            localPlayer.fetchSavedGames { games, error in
                // Iterate saved games
                var game: GKSavedGame?
                for currentGame in games ?? [] {
                    if game == nil || game?.modificationDate ?? Date() < currentGame.modificationDate ?? Date() {
                        game = currentGame
                    }
                }
                
                // If one found, load its data
                if let game = game {
                    game.loadData { data, error in
                        if let data = data {
                            self.gameData = try? JSONDecoder().decode(GameData.self, from: data)
                        }
                        self.isGameLoaded = true
                        self.initGameData()
                        completionHandler()
                    }
                } else {
                    self.isGameLoaded = true
                    self.initGameData()
                    completionHandler()
                }
            }
        }
    }
    
    func saveGame() {
        // Get player
        let localPlayer = GKLocalPlayer.local
        if localPlayer.isAuthenticated, let gameData = gameData, let data = try? JSONEncoder().encode(gameData) {
            // Save its game data
            localPlayer.saveGameData(data, withName: "data") { savedGame, error in
                if let error = error {
                    print(error.localizedDescription)
                }
            }
        }
    }
    
    func initGameData() {
        // If game data is undefined, define it
        if gameData == nil {
            gameData = GameData()
        }
    }
    
    func gameEnded(level: Level, score: Int64) {
        // Here I edit my `gameData` object before saving the changes
        // I known that this method is called because the data is up to date in the UI
        // ...
        saveGame()
    }
    
}

Also, GameCenter is enabled in the app capabilities (and workout for other things like leaderboards)

GameCenter Capability

After restarting the app, and calling loadSavedGame, the gameData property is not restored to its previous state. What is wrong with this code?

Note: I tested it on both Simulator and on my own iPhone (real daily device where GameCenter and iCloud are working with other apps) and it never saves anything.


Solution

  • After enabling iCloud in Capabilities, iCloud Documents, and creating a container for the app, it now works. There is nothing in the documentation about this.

    iCloud Capability