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)
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.
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.