Integrate Game Center features using GameKit. Use when authenticating GKLocalPlayer, checking player restrictions, submitting leaderboard scores, reporting achievements, implementing real-time or turn-based matchmaking, handling GKMatch data, showing the Game Center dashboard or access point, adding challenges and friend invitations, saving game data, or verifying player identity on a server.
SKILL.md
GameKit
Use GameKit for Game Center authentication, competition, matchmaking, social
surfaces, and saved-game handoffs; keep rendering, board logic, and full
SharePlay group-activity design in their owning framework skills.
All GameKit features require the local player to authenticate first. Set the
authenticateHandler on GKLocalPlayer.local early in the app lifecycle.
GameKit calls the handler multiple times during initialization.
import GameKit
func authenticatePlayer() {
GKLocalPlayer.local.authenticateHandler = { viewController, error in
if let viewController {
// Present so the player can sign in or create an account.
present(viewController, animated: true)
return
}
if let error {
// Player could not sign in. Disable Game Center features.
disableGameCenter()
return
}
// Player authenticated. Check restrictions before starting.
let player = GKLocalPlayer.local
if player.isUnderage {
hideExplicitContent()
}
if player.isMultiplayerGamingRestricted {
disableMultiplayer()
}
if player.isPersonalizedCommunicationRestricted {
disableInGameChat()
}
configureAccessPoint()
}
}
Guard on GKLocalPlayer.local.isAuthenticated before calling any GameKit API.
For server-side identity verification, see references/gamekit-patterns.md.
Access Point
GKAccessPoint displays a Game Center control in a corner of the screen. When
tapped, it opens the Game Center dashboard. Configure it after authentication.
Hide the access point during gameplay and show it on menu screens:
GKAccessPoint.shared.isActive = false // Hide during active gameplay
GKAccessPoint.shared.isActive = true // Show on pause or menu
Open the dashboard to a specific state programmatically. Specific leaderboard
access-point triggers require iOS 18+.
// Open directly to a leaderboard
GKAccessPoint.shared.trigger(
leaderboardID: "com.mygame.highscores",
playerScope: .global,
timeScope: .allTime
) { }
// Open directly to achievements
GKAccessPoint.shared.trigger(state: .achievements) { }
Dashboard
Present the Game Center dashboard using GKGameCenterViewController. The
presenting object must conform to GKGameCenterControllerDelegate.
Dashboard states include .dashboard, .leaderboards, .achievements, .challenges, .localPlayerProfile, and .localPlayerFriendsList.
Leaderboards
Configure leaderboards in App Store Connect before submitting scores. Supports
classic (persistent) and recurring (time-limited, auto-resetting) types.
Submitting Scores
Submit to one or more leaderboards using the class method:
GKLeaderboard.Entry provides player, rank, score, formattedScore,
context, and date. For recurring leaderboard timing, leaderboard images,
and leaderboard sets, see references/gamekit-patterns.md.
Achievements
Configure achievements in App Store Connect. Each achievement has a unique
identifier, point value, and localized title/description.
Reporting Progress
Set percentComplete from 0...100. The property type is Double, but Apple requires an integer value. GameKit only accepts increases.
If an achievement is not returned, the player has no progress on it yet. Create
a new GKAchievement(identifier:) to begin reporting. Use
GKAchievement.resetAchievements() to reset all progress during testing.
Real-Time Multiplayer
Real-time multiplayer connects players in a peer-to-peer network for
simultaneous gameplay. Players exchange data directly through GKMatch.
Matchmaking with GameKit UI
Use GKMatchmakerViewController for the standard matchmaking interface:
Data modes: .reliable sends until delivery succeeds or the connection times out; .unreliable sends once and may arrive out of order. Use .reliable for critical state and .unreliable for small, time-sensitive updates. Treat received match data as untrusted input. Register the local player as a listener (GKLocalPlayer.local.register(self)) to receive invitations. For programmatic matchmaking and custom match UI, see references/gamekit-patterns.md.
Turn-Based Multiplayer
Turn-based games store match state on Game Center servers. Players take turns
asynchronously and do not need to be online simultaneously.
// DON'T: Set handler on every scene transition
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
GKLocalPlayer.local.authenticateHandler = { vc, error in /* ... */ }
}
// DO: Set the handler once, early in the app lifecycle
// DON'T: Set delegate in dismiss completion -- misses early messages
func matchmakerViewController(_ vc: GKMatchmakerViewController, didFind match: GKMatch) {
vc.dismiss(animated: true) { match.delegate = self }
}
// DO: Set delegate before dismissing
func matchmakerViewController(_ vc: GKMatchmakerViewController, didFind match: GKMatch) {
match.delegate = self
vc.dismiss(animated: true)
}
Not calling finishMatchmaking for programmatic matches
// DON'T
let match = try await GKMatchmaker.shared().findMatch(for: request)
startGame(with: match)
// DO
let match = try await GKMatchmaker.shared().findMatch(for: request)
GKMatchmaker.shared().finishMatchmaking(for: match)
startGame(with: match)
GKLocalPlayer.local.authenticateHandler set once at app launch
isAuthenticated checked before any GameKit API call
Player restrictions checked (isUnderage, isMultiplayerGamingRestricted, isPersonalizedCommunicationRestricted)
Game Center capability added in Xcode signing settings
Leaderboards and achievements configured in App Store Connect
Access point configured and toggled appropriately during gameplay
GKGameCenterControllerDelegate dismisses dashboard in gameCenterViewControllerDidFinish
Match delegate set immediately when match is found
finishMatchmaking(for:) called for programmatic matches; disconnect() and nil delegate on exit
Turn-based match data stays under match.matchDataMaximumSize
Turn-based participants have outcomes set before endMatchInTurn
Invitation or turn listener registered with GKLocalPlayer.local.register(_:)
Data mode chosen appropriately: .reliable for state, .unreliable for frequent updates
Error handling for all async GameKit calls
References
See references/gamekit-patterns.md for identity verification, legacy voice chat, saved games, custom match UI, leaderboard images, challenge handling, and rule-based matchmaking.