swift 5: gamekit leaderboard

Games on the Apple App Store are abundant. We all know this. That’s why it’s imperative to put on your competitive hat and face the action (a little marketing never hurt also). What better way than to introduce some social dynamics to your game via a competitive leaderboard?

We’ve been busy developing a new game and find it incredibly easy to implement basic social functionality using the native Apple Game Center (with GameKit). Most games use this to some extent, so we wanted to share with you all a quick way to implement a GameKit leaderboard.

For reference, GameKit is the framework that allows us to interact with Apple Game Center. You can read more about it in the Apple Developer Documentation.

Before we get started, we wanted to outline the steps at a high level:

  1. Enable GameKit in your Xcode project
  2. Create a Leaderboard Identifier on App Store Connect
  3. Write some code to read and write to Game Center

Very straightforward. Let’s dive right in.

Enable GameKit in your Xcode project

Start by opening your application and verifying the bundle identifier. To enable GameKit, all that’s required is to click the ‘+ Capability’ button on your target. The steps are illustrated in the image below.

That’s it. Your application is set. Let’s set up the leaderboard in your App Store Connect account.

Create a Leaderboard Identifier

The next step is to create a unique leaderboard on your App Store Connect account. Go through the process of creating an app. Once finished, navigate to the Game Center tab under Features. Click the ‘+’ next to Leaderboard, select a single leaderboard, and populate the required information. We use the com.companyname.appname naming convention for our in-app purchases and Game Center content. Make note of the leaderboard ID as we’ll need to reference it when querying from our application.

Write the Code

The final step is to implement the necessary lines of code. What we want to accomplish:

  1. Authenticate the user with Game Center – Apple suggests we do this immediately
  2. Track a scoring metric and posting the high score – we need to track something (we’ll use a high score for this example)
  3. Load the leaderboard – we need a way to display the leaderboard results

It’s not too difficult to implement, however, there are a few hiccups that can occur along the way, which we’ll discuss below.

Authenticate the user with Game Center

Let’s start by authenticating the user when our root view controller’s view did load.

class GameViewController: UIViewController {
    
override func viewDidLoad() {
        super.viewDidLoad()
        authenticatePlayer()
    }

    func authenticatePlayer() {
        if SWGameManager.sharedGameDataManager.gameCenterDisabled == true {
            presentGameCenterDisabledAlert()
            return
        }
        
        let localPlayer = GKLocalPlayer.local
        localPlayer.authenticateHandler = { (viewController, error) -> Void in
            if ( viewController != nil ) {
                self.present(viewController!, animated: true, completion: nil)
            } else if GKLocalPlayer.local.isAuthenticated == true {
                SWGameManager.sharedGameDataManager.gameCenterDisabled = false
                print("Player is authenticated!")
            } else {
                SWGameManager.sharedGameDataManager.gameCenterDisabled = true
            }
        }
    }
}

This is all the code it takes to authorize a user. The SWGameManager code manages additional app-specific metrics (game mechanics, etc), however, the gameCenterDisabled property is one we’d recommend. For whatever reason, if a user declines to log in to Game Center multiple times, the authenticateHandler method won’t be called anymore. This requires the user to go into their general settings and enable Game Center manually. We track this with a flag and then notify the user if their Game Center is disabled.

Track a scoring metric and posting the high score

If you’re developing a game, then chances are you’re tracking some sort of score. When it comes time to sending the score to Game Center, then you’ll need to implement some variation of the following code:

    func postHighScoreToLeaderboard() {
        if GKLocalPlayer.local.isAuthenticated {
            print("\n Success! Sending highscore of \(score) to leaderboard")
            
            let scoreReporter = GKScore(leaderboardIdentifier: Constants.kGameKitLeaderboardID)
            
            scoreReporter.value = Int64(SWGameManager.sharedGameDataManager.highscore)
            let scoreArray: [GKScore] = [scoreReporter]
            
            GKScore.report(scoreArray) { (error) in
                if error != nil {
                    print("An error has occurred:")
                    print("\n \(error!) \n")
                    self.delegate?.postToHighScoreLeaderboardDidFail()
                } else {
                    self.delegate?.postToHighScoreLeaderboardDidSucceed()
                }
            }
            
        } else {
            self.delegate?.localPlayerNotAuthenticatedToPostHighScore()
        }
    }

What we’re doing here is:

  • Verifying that the user is authenticated
  • Creating a score object using our leaderboard identifier (the same one we used to create the leaderboard on App Store Connect)
  • Reporting the score and handling the output

We rely on delegate methods to handle the different outcomes (the functions labeled self.delegate?). These aren’t necessary but are definitely convenient.

Load the Leaderboard

Finally, it’s time to display the leaderboard. This is incredibly straightforward. All that’s required is to use the following code and implement the required delegate method.

    // Present the leaderboard scene
    func presentHighScoreLeaderboard() {
        if SWGameManager.sharedGameDataManager.gameCenterDisabled == true {
            presentGameCenterDisabledAlert()
        } else {
            let leaderViewController = GKGameCenterViewController()
             leaderViewController.gameCenterDelegate = self // Make sure to implement the delegate method
             leaderViewController.leaderboardIdentifier = Constants.kGameKitLeaderboardID
             self.show(leaderViewController, sender: self)
        }
    }

Wrap up

That should do it. Now use a sandbox Apple ID to debug! While this was a relatively simple exercise, Game Center is much more powerful (e.g. tracking player achievements, sharing leaderboards, etc). Check out the Apple Developer Documentation for more information and stay tuned for additional write-ups!