Search code examples
swiftsprite-kitgame-centergame-center-leaderboard

GameCenter scores are not being posted to the leaderboard


Bear with me here, as there is a lot to explain!

I am working on implementing a Game Center high score leaderboard into my game. I have looked around for examples of how to properly implement this code, but have come up short on finding much material. Therefore, I have tried implementing it myself based off information I found on apples documentation.

Long story short, I am getting success printed when I update my score, but no scores are actually being posted (or at-least no scores are showing up on the Game Center leaderboard when opened).

Before I show the code, one thing I have questioned is the fact that this game is still in development. In AppStoreConnect, the status of the leaderboard is "Not Live". Does this affect scores being posted?

Onto the code. I have created a GameCenter class which handles getting the leaderboards and posting scores to a specific leaderboard. I will post the code in whole, and will discuss below what is happening.

class Leaderboard
{
   var id          : String
   var leaderboard : GKLeaderboard
   var loaded      : Bool

   init()
   {
       self.id          = ""
       self.leaderboard = GKLeaderboard()
       self.loaded      = false
   }
 
   init(id: String, leaderboard: GKLeaderboard, loaded: Bool)
   {
       self.id          = id
       self.leaderboard = leaderboard
      self.loaded      = loaded
   }
}

class GameCenter
{
   static let shared = GameCenter()

   private var player = GKLocalPlayer.local
   private var leaderboards : [Leaderboard] = []

   func authenticatePlayer()
   {
      player.authenticateHandler = { (vc, error) -> Void in
         if let error = error
         {
            print(error.localizedDescription)
         }
         else if let vc = vc
         {
            if let viewController = UIApplication.shared.windows.first!.rootViewController
            {
               viewController.present(vc, animated: true)
               {
                  self.loadLeaderboards()
               }
            }
         }
         else
         {
            self.loadLeaderboards()
         }
      }
   }

   func loadLeaderboards()
   {
      var leaderboardIDs : [String] = []
    
      // Gather all of the leaderboard ids that we have
      for leaderboard in GameCenterLeaderboards.allCases
      {
         leaderboardIDs.append(leaderboard.rawValue)
      }
    
      // Load the leaderboard for all of these ids and add to a new array
      GKLeaderboard.loadLeaderboards(IDs: leaderboardIDs) { (loadedLeaderboards, error) in
        
         if let error = error
         {
            print(error.localizedDescription)
            return
         }
        
         if let loadedLeaderboards = loadedLeaderboards
         {
            print("\n--- Loaded Leaderboards ---")
            for loadedBoard in loadedLeaderboards
            {
               let board = Leaderboard(id: loadedBoard.baseLeaderboardID, leaderboard: loadedBoard, loaded: true)
               self.leaderboards.append(board)
                
               print("ID: \(board.id)")
            }
            print("\n")
            
            self.updateLocalHighScore()
         }
      }
   }

   func playerAuthenticated() -> Bool
   {
      return player.isAuthenticated
   }

   func submitScore(id: String)
   {
      if ( playerAuthenticated() )
      {
         let leaderboard = getLeaderboard(id: id)
         if ( leaderboard.loaded )
         {
            print("Submitting score of \(AppSettings.shared.highScore!) for leaderboard \(leaderboard.id)")
            leaderboard.leaderboard.submitScore(AppSettings.shared.highScore, context: -1, player: player) { (error) in
               if let error = error
               {
                  print(error.localizedDescription)
               }
               else
               {
                  print("Successfully submitted score to leaderboard")
               }
            }
         }
      }
   }

   func getLeaderboard(id: String) -> Leaderboard
   {
      if let leaderboard = leaderboards.first(where: { $0.id == id } )
      {
         return leaderboard
      }
    
      return Leaderboard()
   }

   func updateLocalHighScore()
   {
      let leaderboard = getLeaderboard(id: GameCenterLeaderboards.HighScore.rawValue)
      if ( leaderboard.loaded )
      {
         leaderboard.leaderboard.loadEntries(for: [player], timeScope: .allTime) { (playerEntry, otherEntries, error) in
            
            if let error = error
            {
               print(error.localizedDescription)
               return
            }
            
            if let score = playerEntry?.score
            {
               print("Player Score in leaderboard: \(score)")
               if( score > AppSettings.shared.highScore )
               {
                  AppSettings.shared.highScore = score
                  print("High Score Updated!")
               }
               else
               {
                  // Lets post the local high score to game center
                  self.submitScore(id: leaderboard.id)
                  print("Local High Score Is Greating, requesting a submit!")
               }
            }
         }
      }
   }
}

In a different GameScene, once the game is over, I request to post a new high score to Game Center with this line of code:

GameCenter.shared.submitScore(id: GameCenterLeaderboards.HighScore.rawValue)

The last thing I have questions about is the context when submitting a score. According to the documentation, this seems to just be metadata that GameCenter does not care about, but rather something the developer can use. Therefore, I think I can cross this off as causing the problem.

I believe I implemented this correctly, but for some reason, nothing is posting to the leaderboard. This was ALOT, but I wanted to make sure I got all my thoughts down.

Any help on why this is NOT posting would be awesome! Thanks so much!

Mark


Solution

  • I had the same problem - submitting scores to Game Center by using a new GameKit API available from iOS 14 was never actually saved on the Game Center side (even though there was no any error reported).

    Solution which worked for me in the end was simply using a Type Method (with my leaderboardID):

    class func submitScore(_ score: Int, 
                   context: Int, 
                    player: GKPlayer, 
            leaderboardIDs: [String], 
         completionHandler: @escaping (Error?) -> Void)
    

    instead of the Instance Method counterpart (which apparently has some bug on Apple side):

    func submitScore(_ score: Int, 
             context: Int, 
              player: GKPlayer, 
    completionHandler: @escaping (Error?) -> Void)
    

    I was going crazy over this, so I hope this helps someone else.