Turn-Based Matches
In a turn-based match, the participants of the match do not need to be simultaneously connected to Game Center to play a match together. Instead, the game uses a store-and-forward approach; one player takes a turn before passing the next play to another player. The players continue to take turns until the match ends.
Turn-based matches have many useful characteristics that make them great for implementing board games and other similar styles of games; for example:
A player can participate in multiple matches simultaneously. A game loads whichever match the player is interested in viewing or advancing.
Players must connect to Game Center only to take a turn.
A match can be created with less than a full complement of players, even a single player. Other players are added as needed.
When you implement turn-based matches in your game, the list of participants, the data for matches and other details are all stored on Game Center. Your game downloads this information as needed. Game Center is primarily responsible for storing data. You are responsible for providing the game logic that uses this infrastructure. In particular, you define:
what data must be stored on Game Center.
when the match data needs to be updated.
when play passes to another participant.
Checklist for Implementing a Turn-Based Match
To add turn-based support to your game, you write code to handle the following activities:
Allow the local player to join a match. See “Joining a New Match.”
Allow the local player to see the list of existing matches. See “Working With Existing Matches.”
Allow the local player to view the state of a match in progress. See “Working With Match Data.”
Allow the player to take a turn in the match. See “Advancing the State of the Match.”
When a player leaves a match, set the participant’s match outcome. See “Setting the Match Outcome when a Participant Leaves a Match”
When all the players have a match outcome set, end the match. See “Ending a Match.”
Handle invitations and other match events. See “Responding to Match Notifications.”
Every Match Has a List of Participants
You can think of a match on Game Center as a board game in the middle of a table. Around the table are empty seats, waiting to be filled by a potential participant. The number of participants is set when the match is first created and never changes.
When a match begins, only some of the seats may be filled by players. The other seats may be reserved for specific players or to be filled by Game Center’s matching service. For example, consider Figure 10-1. Mary has created a new match with four seats. Because Mary created the match, she the first turn and then play passes to another participant.

At this point, Game Center sees that this match needs a new player to continue play. When Bob searches for a match, Game Center assigns Bob as a new participant of Mary’s match. Because this game was waiting on a new player to continue the match, it is already Bob’s turn. Essentially, whether a new match is created or the player is placed in an existing match, it is always that player’s turn at the point at which they join the match.

Game Center always tracks the participants of a match in a specific order. That order never changes for the duration of the match, regardless of which device your game is running on. In this example, if Mary is in the first seat (slot), she stays in the first seat for the entire game.
Every match has the concept of the current participant. The current participant changes throughout the match and represents the player whose turn it is to act. When that player finishes taking a turn, your game chooses another player to act. That player is notified that it is now his or her turn.
The Match Data Represents the State of the Match
Your game sends match data to Game Center which describes the state of a match. Game Center sets a limit on the size of this match data but otherwise does not care about its contents. Your game should store whatever data is necessary to preserve the match state.
At any time, a player in the match can launch your game to view a match. When a player does this, your game loads the match data, interprets it and displays it to the player.

Only the current participant is allowed to change the match data. This player should be shown a user interface that allows them to take actions in the game. As the player takes actions, your game updates the match data and transmits it back to Game Center.

Your Game Decides The Rules of Play
When you design a turn-based game, you create the rules that dictate play. You decide exactly what actions a player is allowed to take and when play passes to another participant. When play passes to another participant, you decide which participant plays next.
Consider chess. The player with the white pieces moves first. After white moves, play passes to the participant with the black pieces. After black makes a move, play passes back to white. The participants alternate until the match ends in either a checkmate or a stalemate. The rules dictate that play alternates and each player makes a single move each time.
The rules that chess uses for players are very consistent, but Game Center allows your own designs to be more flexible. Each time a player takes a turn, you can show different user interface screens and present the player different options for play. A player’s turn could be as simple as making a single decision or it could be multiple decisions. Your game tracks the moves a player takes and the moves he or she is allowed to make as part of your match data.
Consider another popular style of strategy game: the 4X game that encourages players to explore, expand, exploit and exterminate. Here, you might need multiple kinds of turns used at different points in the match. For example, here is one possible way you might organize this:
At the start of the match, each player takes a starting turn. Play passes sequentially through the entire participant list. On a starting turn, a player action consists of naming the player’s empire and choosing options for how they intend to play in the match. For example, a common 4X trope is for a player to choose a faction that provides specific advantages and disadvantages during play. Once all the players have completed their starting turns, the match begins by allowing players to administrate their empires.
On a administrative game turn, each active player takes a turn, and play passes sequentially though the participant list. Each participant performs all the actions necessary to control his or her empire. After all participants complete their orders, the administrative turn ends. Your game then executes all the orders simultaneously, usually on the client of the last participant to take an administrative turn. If any player units come into conflict, then a conflict game turn is executed, otherwise a new administrative game turn starts.
On a conflict game turn, the game creates a list of all of participants involved in conflicts. Then, each player on the list is given a turn to allow them to give combat orders. After all players give orders, the orders are executed simultaneously and the conflicts are resolved at once. If any players are eliminated as a result of combat, the game takes them out of the match on future turns. If only one player remains active in the match, the match ends and that player is declared the winner. Otherwise, play continues with another administrative game turn.
Figure 10-3 shows how this hypothetical game’s logic is processed. It has three distinct kinds of player turns, each with a different user interface, possible user actions and data to be stored in the match data.

Whenever a participant takes a turn, the match data indicates what kind of turn the player is supposed to take. After a player takes a turn, his or her actions are stored in the match data. The design also avoids passing turns to other participants for non-trivial reasons. Every player makes multiple decisions at once before passing control to another player. When the final player completes a turn, that player’s game client processes everyone’s turns simultaneously, writing any necessary changes to the match data. Although this kind of design is not required, it does keep the game moving by avoiding small trivial player turns.
Save the Match Data Whenever Important Events Occur
The current participant’s device can save the data to Game Center immediately whenever any action is taken, without transferring control to another participant. You should save match data whenever anything interesting happens. Consider the following list as critical places where data should be sent to Game Center:
The action should be immediately visible to other participants if those participants choose to look at the match.
The action should be irrevocable, such as when the action reveals information previously hidden from the player or generates a randomized result that needs to be persistent.
The action changes the current participant.
Implementing a Turn-Based Match Using Game Kit
To implement turn-based support in your game, you use the classes listed in Table 4-3.
Class Name | Class Function |
|---|---|
Describes a turn-based match stored on Game Center. You use this object to inspect the current state of the match and to update the state of the match when the current participant takes a turn. | |
Describes the characteristics of a match and possible includes an initial list of players to invite. | |
Describes a participant in a match. Most of its properties are read-only, but the | |
Provides a standard user interface that allows the local player to create new matches or view existing matches. | |
Handles events when the player receives an invitation to join a match or a notification that an existing match has been updated. There is a single event handler object provided to you by Game Kit. After the player is authenticated, you assign an event handler to this singleton object. The exact events delivered to your game vary depending upon whether your game is currently the foreground app on the device. When your game is in the foreground, it receives all events related to turn-based matches. If not running in the foreground, your game is launched or brought to the foreground only when important events are received. |
Game Center Imposes Limits on the Match
Table 10-4 describes some of the important limits you need to consider when designing your game. These limits are subject to change and can both be queried at runtime.
Item | Limit | Runtime Access |
|---|---|---|
Number of Players | 16 | Call the |
Size of Match Data | 64k | Read the |
Joining a New Match
For players, a new match begins when the player joins a match. As with other forms of matchmaking, your game describes the match it wants to create by creating a GKMatchRequest object, as described in “Creating Any Kind of Match Starts with a Match Request.” A match request does not guarantee that a new match is going to be created; it may also match the player into an existing match that has an empty position as the current participant.
You can choose to display a standard user interface provided by Game Center or implement your own custom user interface. Regardless of which technique you use, the match returned to your game always has the local player as the current participant expected to take a turn.
Showing the Standard Matchmaking User Interface
You use a GKTurnBasedMatchmakerViewController object to display the standard user interface. Listing 10-1 shows a typical implementation.
Listing 10-1 Displaying the standard interface to join a turn-based match
- (IBAction)joinChessMatch: (id) sender |
{ |
GKMatchRequest *request = [[GKMatchRequest alloc] init]; |
request.minPlayers = 2; |
request.maxPlayers = 2; |
GKTurnBasedMatchmakerViewController *mmvc = [[GKTurnBasedMatchmakerViewController alloc] initWithMatchRequest:request]; |
mmvc.turnBasedMatchmakerDelegate = self; |
[self presentViewController:mmvc animated:YES completion:nil]; |
} |
The presenting view controller in this example implements the GKTurnBasedMatchmakerViewControllerDelegate pattern. For matchmaking, you need to handle three of the protocol’s methods.
The turnBasedMatchmakerViewControllerWasCancelled: delegate method is called when the player dismisses the matchmaking screen by tapping the Cancel button. In most cases, you should simply return to the previous screen in your game. A minimal version of this code can be found in Listing 10-2.
Listing 10-2 Implementing the cancellation method
- (void)turnBasedMatchmakerViewControllerWasCancelled:(GKTurnBasedMatchmakerViewController *)viewController |
{ |
[self dismissViewControllerAnimated:YES completion:nil]; |
} |
The turnBasedMatchmakerViewController:didFailWithError: delegate method is called when matchmaking encounters an error while trying to find the match. For example, the device may have lost its network connection. Your implementation should also return to your game’s previous screen. A minimal version of this code can be found in Listing 10-3.
Listing 10-3 Implementing the error handling method
- (void)turnBasedMatchmakerViewController:(GKTurnBasedMatchmakerViewController *)viewController didFailWithError:(NSError *)error |
{ |
[self dismissViewControllerAnimated:YES completion:nil]; |
} |
Finally, the turnBasedMatchmakerViewController:didFindMatch: method is called when a match has been found or created. The match object is returned to your delegate. Typically, your game dismisses the matchmaker view controller and then immediately its own user interface to allow the player to play a turn. The implementation in Listing 10-4, uses a custom segue to show the gameplay screen. The match is passed as the sender so that it can be passed as an input to the new view controller.
Listing 10-4 Implementing the match found method
- (void)turnBasedMatchmakerViewController:(GKTurnBasedMatchmakerViewController *)viewController didFindMatch:(GKTurnBasedMatch *)match |
{ |
[self dismissViewControllerAnimated:YES completion:nil]; |
[self performSegueWithIdentifier:@"GamePlayScene" sender:match]; |
} |
- (void )prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender |
{ |
if ([segue.identifier isEqualToString:@"GamePlayScene"]) |
{ |
MyGamePlayViewController* gameVC = (MyGamePlayViewController*) segue.destinationViewController; |
gameVC.delegate = self; |
gameVC.match = (GKTurnBasedMatch*) sender; |
} |
} |
Implementing a Custom Match Interface
To create your own custom match interface, you typically use methods on the the GKTurnBasedMatch class. Listing 10-5 shows a trivial implementation of this technique. It creates a new match request and then uses it find a new match. (Typically, if your game was implementing a custom match interface, it might set other properties of the request object, such as the playersToInvite property.) If a match is found, it transitions to the game screen.
Listing 10-5 Finding a turn-based match programmatically
- (IBAction)findProgrammaticMatch: (id) sender |
{ |
GKMatchRequest *request = [[GKMatchRequest alloc] init]; |
request.minPlayers = 4; |
request.maxPlayers = 16; |
[GKTurnBasedMatch findMatchForRequest: request withCompletionHandler:^(GKTurnBasedMatch *match, NSError *error) |
{ |
if (match) |
[self performSegueWithIdentifier:@"GamePlayScene" sender:match]; |
}]; |
} |
One common scenario is supported automatically by Game Center. When a match ends, you can call its rematchWithCompletionHandler: instance method to create a new match with the same participants.
Handling Invitations Programmatically
If a match request includes a list of players to invite, then those players are added to the list of participants in the newly created match. However, invitations are not actually delivered until one of those participants becomes the current participant. When that happens, a push notification is sent to the player. The process for handling notifications is described later in “Responding to Match Notifications.” For now, all you need to know is that you can display your own interface that allows the player to accept or decline the match invitation. You call the match object’s acceptInviteWithCompletionHandler: method to accept the invitation or its declineInviteWithCompletionHandler: method to decline the invitation.
Working With Existing Matches
If you display the standard matchmaking user interface, then the player also sees existing matches as well. The player can choose an existing match already in progress and perform other common tasks. If the player picks an existing match, the turnBasedMatchmakerViewController:didFindMatch: method is called, exactly as it did when a new match was created. Note that in this case, the player may not be the current participant.
A player can choose to resign from a match from within the standard user interface. Your delegate must implement a turnBasedMatchmakerViewController:playerQuitForMatch: method to handle a player resignation. Your game must set a match outcome for the resigning player and if necessary choose another player to act in the match. For more information, see “Setting the Match Outcome when a Participant Leaves a Match.”
If you implemented a custom matchmaking user interface, you need to provide equivalent functionality that allows a player to manage the list of matches. Listing 10-6 shows how to retrieve a list of matches that the local player is participating in.
Listing 10-6 Retrieving the list of matches the local player is participating in
- (void) loadMatches |
{ |
[GKTurnBasedMatch loadMatchesWithCompletionHandler:^(NSArray *matches, NSError *error) { |
if (matches) |
{ |
// Use the match data to populate your user interface. |
} |
}]; |
} |
Table 10-3 lists the most common actions a player might want to perform on a match.
Action | Implementation |
|---|---|
View a match | Present your gameplay user interface, using the match object as an input. See Listing 10-4 for inspiration on how to implement this. |
Delete a completed match | Call the match object’s |
Resign from a match | Set the participant’s outcome and then call a method to resign the match. See “Setting the Match Outcome when a Participant Leaves a Match.” |
End a match for all participants | Set all the participant’s outcomes and then call the match’s |
Create a rematch | Call the |
Retrieving Information About a Match
When a player decides to view a match, your game reads the properties of the match to determine the current state of the match. Table 10-4 lists the most common properties you need to access.
Property | Description |
|---|---|
A string that uniquely identifies the match. If you want to store match-specific data elsewhere, use this string as a key. Your game could also save this ID and use it later to load this specific match. To load a specific match, call the | |
A status field that tells you whether the match is still in progress. | |
A text string your game sets to provide a human-readable status for the match. Typically, you update this property before changing the current participant. The message is displayed by the standard user interface. If you display a custom interface, you should also display the message. | |
An array of | |
The participant object for the next player expected to act in the match. This object is always one of the objects stored in the |
Property | Description |
|---|---|
The player identifier for the player, assuming this seat is filled. Use it to load display names and photos for the player. | |
Declares whether this seat is filled or not and if filled, whether the player is still active in the match. | |
The time at which the player must act before forfeiting a turn. Your game decides what happens when a turn is forfeited. For some games, a forfeited turn might end the match. For other games, you might choose a reasonable set of default actions for the player, or simply do nothing. | |
When a player leaves a match, this property describes what happened to the player. The player may have won or lost the match. |
Working With Match Data
When a match is first created, the match data is empty. But once the match begins and players start taking turns, your game is expected to provide match data that makes sense for your game. The match data is an NSData object and its size is limited, so you need to be creative and encode your game’s data so that it fills as little space as possible.
In general, the match data needs to store enough information so that your game can display the current state of the match. If the player is the current participant, then the match data should also store enough data so that your game knows what kind of turn the player may take. Here are a few possible strategies you can follow when designing your match data format:
Encode only player actions: In this design, your match data simply consists of the moves made by the players. For example, in chess, you know that white goes first, moves always alternate, and that a piece moves from one board position to another. This makes it easy for you to encode each move as the starting and ending position of the piece moved. The rest of the data (who made the moves) can be completely inferred. When your game loads the match data, it quickly replays the moves to generate the current board position.
This strategy works best for games with a small number of possible kinds of actions and a small number of moves per match. Also, with this model, it is very possible for your game to replay the moves in its user interface, allowing players to see exactly what moves other opponents made to get the board into the new state. Showing a player these moves makes it very easy for a player to understand how the game got to the current state.
Encode only the current state of the match: For very complex games, the actual state required to encode the game could be very large. In this case, you may need to encode the current state of the match without worrying about the actions that generated that match data. This is particularly true for very complex games where the list of moves might grow too large to fit in the available storage.
This strategy is recommended as a last restort. Payers lose all of the context of what happened on previous turns of the match. For games with long timeouts between turns, players may grow bored or frustrated if they cannot remember the state of a match they were playing. This is particularly true when players participant in multiple matches simultaneously.
Encode the current state of the match and a set of recent player actions: This is a hybrid strategy that borrows elements from the other two strategies. Essentially, the match date stored on Game Center consists of a recent snapshot of the match plus other data that encodes recent actions since that last snapshot was taken. When the data that records the actions grows too large, your game replays some of those moves permanently to update the match snapshot and then deletes those moves from its list of actions.
With this strategy, you typically need to determine which actions the current participant has seen (based on when they last took a turn). When your game flattens the match data, it never removes any data that hasn’t been seen by all the participants. This allows all participants to see the moves their opponents made.
And here are some other general guidelines:
Avoid using the
NSCoderclass except for the most trivial of games. Although useful for general purpose apps, theNSCoderclass may not archive data in a compact enough format for your game. You cannot precisely predict the size of the archive it produces. Instead, you should allocate your own block of memory and perform your own data serialization.Encode individual items as compactly as needed. For example, in a 16-player game, you can encode the participant number of the player in 4-bits. Balance the need for compactness with the need for readability in your code. For example, a chess position could be encoded in as little as 6 bits, but in practice a chess match does not approach the match data limit.
If your game runs on both OS X and iOS, use network data encoding techniques to avoid byte ordering problems.
Loading Match Data
When you need to load the match data, you call the match object’s loadMatchDataWithCompletionHandler: method to retrieve the match data. When the completion handler is called, you receive the match data. After the operation completes, the match object’s matchData property is also updated to point at the same data object.
Listing 10-7 Loading the match data From Game Center
- (void) loadAndDisplayMatchData |
{ |
[this.myMatch loadMatchDataWithCompletionhandler: ^(NSData *matchData, NSError *error) { |
if (matchData) |
{ |
// App-specific routine to decode the match data. |
this.gameData = [MyMatchDataClass initWithData: matchData]; |
// Display the match. |
} |
}]; |
} |
Saving Match Data
If the player takes an action that is irrevocable (but control is not yet passed to another player), you encode the updated match data. Then, you send it to Game Center by calling the saveCurrentTurnWithMatchData:completionHandler: method.
Listing 10-8 Saving the match data to Game Center
- (void) updateMatchData |
{ |
// App-specific routine to encode the match data. |
NSData *updatedMatchData = [this.gameData encodeMatchData]; |
[this.myMatch saveCurrentTurnWithMatchData: updatedMatchData completionHandler ^(NSError *error) { |
if (error) |
{ |
// Handle the error. |
} |
}]; |
} |
Advancing the State of the Match
Eventually, the current participant takes an action that either ends the match or requires another participant to act. Typically, your game updates the message property of the match object, encodes the match data, and determines who acts next in the match. Usually, this means encoding the list of all participants in the match in the order they will act next. The reason you do this is because sometimes players drop from a match without forfeiting; they just stop playing. You don’t want the match to end because it is stuck waiting forever for an absent player. Instead, you encode the list of participants so that if one participant forfeits a turn, the match advances to another participant.
Listing 10-9 shows a typical structure for this code, deferring some of the specific steps to app-specific routines that are based on your game’s actual structure. Those methods represent the places where you need to provide specific implementations in your game. The advanceTurn method first sets a new message, then encodes the current match data as before. Finally, it calculates the order in which participants should act and calls the endTurnWithNextParticipants:turnTimeout:matchData:completionHandler: method to actually pass control to that list of players.
Listing 10-9 Advancing to the next participant in a match
- (void) advanceTurn |
{ |
NSData *updatedMatchData = [this.gameData encodeMatchData]; |
NSArray *sortedPlayerOrder = [this.gameData encodePlayerOrder]; |
this.MyMatch.message = [this.gameData matchAppropriateMessage]; |
[this.myMatch endTurnWithNextParticipants: sortedPlayerOrder turnTimeOut: GKTurnTimeoutDefault |
matchData: updatedMatchData completionHandler ^(NSError *error) { |
if (error) |
{ |
// Handle the error. |
} |
}]; |
} |
Setting the Match Outcome when a Participant Leaves a Match
As the match progresses, participants may leave the match. For example, your game logic might determine that a participant has been eliminated from the match.
If the local player resigns from the match and is also the match’s current player, your game must call the match object’s participantQuitInTurnWithOutcome:nextParticipants:turnTimeout:matchData:completionHandler: method. This method is similar to the endTurnWithNextParticipants:turnTimeout:matchData:completionHandler: method in that it gives control to another participant. Because of this, you are required to update the match data and provide a participants list. However, your game also provides a match outcome for the player that just exited the match — essentially, a numerical value that indicates why that player left the match. The participant object for that player is updated to include this new match state. Once a participant has exited the match, your game may not make that player the current participant.
Game Kit provides some standard values you can use to set the match outcome. See “Setting the Match Outcome” in GKTurnBasedParticipant Class Reference.
For example, in Figure 10-4, Bob has just been eliminated from the match. The game chooses to set an outcome of GKTurnBasedMatchOutcomeFourth and make Mary the new current participant. Bob may still view the match, but may not take actions.

Occasionally a player may resign the game when they are not the current participant. To handle this, your game calls the match object’s participantQuitOutOfTurnWithOutcome:withCompletionHandler: method. You provide a match outcome but do not provide new match data or a participants list.
Ending a Match
Eventually, your game logic is going to decide that the match is over. All participants in the match must have a valid match outcome before ending the match. Your game calls the match object’s endMatchInTurnWithMatchData:completionHandler: method to formally end the match. This method is similar to the other methods that update the match state, but in this case you do not provide a list of participants because no further actions are allowed in this match. You do update the saved match data to describe how the match ended. Players can still select this match from the matchmaking user interface; your game should display the match ending but not allow the player to take actions.

Responding to Match Notifications
A player can receive notifications about turn-based matches. For example, the most common notification a player receives is when that player becomes the current participant for a match (and is expected to take action). In all cases, after a player accepts a notification, your game is called to handle the event. It should process the event and display an appropriate user interface specific to your game.
Your game receives turn-based match events by installing an event handler. As with other similar handlers, you almost always install this handler as soon as the local player is authenticated. If your game was launched specifically to handle an event, the event handler is called immediately.
- (void) installTurnBasedEventHandler |
{ |
[GKTurnBasedEventHandler sharedEventHandler].delegate = self; |
} |
To respond to events, your event handler implements the methods defined by the GKTurnBasedEventHandlerDelegate protocol. Table 10-6 lists the events. Events received via a push notification launch your game or bring your game to the foreground (if necessary) and are then delivered to the game. Foreground events are only delivered if your game was running in the foreground when the event was received.
When an event is received, your game should pause what it is doing and display a user interface that allows the player to decide whether to load the notification’s match for display. Even if this match is already loaded, you should reload the match’s data, because the match’s data may have been updated by the event.
Event | Event Type | Event Handler Method |
|---|---|---|
Player receives an invitation | Push | |
It becomes the player’s turn | Push | |
A match ended | Push | |
It becomes someone else’s turn | Foreground | |
A player updated the match data | Foreground |
© 2012 Apple Inc. All Rights Reserved. (Last updated: 2012-09-19)