$npx -y skills add dpearson2699/swift-ios-skills --skill gamekitIntegrate 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
| 1 | # GameKit |
| 2 | |
| 3 | Use GameKit for Game Center authentication, competition, matchmaking, social |
| 4 | surfaces, and saved-game handoffs; keep rendering, board logic, and full |
| 5 | SharePlay group-activity design in their owning framework skills. |
| 6 | |
| 7 | ## Contents |
| 8 | |
| 9 | - [Authentication](#authentication) |
| 10 | - [Access Point](#access-point) |
| 11 | - [Dashboard](#dashboard) |
| 12 | - [Leaderboards](#leaderboards) |
| 13 | - [Achievements](#achievements) |
| 14 | - [Real-Time Multiplayer](#real-time-multiplayer) |
| 15 | - [Turn-Based Multiplayer](#turn-based-multiplayer) |
| 16 | - [Common Mistakes](#common-mistakes) |
| 17 | - [Review Checklist](#review-checklist) |
| 18 | - [References](#references) |
| 19 | |
| 20 | ## Authentication |
| 21 | |
| 22 | All GameKit features require the local player to authenticate first. Set the |
| 23 | `authenticateHandler` on `GKLocalPlayer.local` early in the app lifecycle. |
| 24 | GameKit calls the handler multiple times during initialization. |
| 25 | |
| 26 | ```swift |
| 27 | import GameKit |
| 28 | |
| 29 | func authenticatePlayer() { |
| 30 | GKLocalPlayer.local.authenticateHandler = { viewController, error in |
| 31 | if let viewController { |
| 32 | // Present so the player can sign in or create an account. |
| 33 | present(viewController, animated: true) |
| 34 | return |
| 35 | } |
| 36 | if let error { |
| 37 | // Player could not sign in. Disable Game Center features. |
| 38 | disableGameCenter() |
| 39 | return |
| 40 | } |
| 41 | |
| 42 | // Player authenticated. Check restrictions before starting. |
| 43 | let player = GKLocalPlayer.local |
| 44 | |
| 45 | if player.isUnderage { |
| 46 | hideExplicitContent() |
| 47 | } |
| 48 | if player.isMultiplayerGamingRestricted { |
| 49 | disableMultiplayer() |
| 50 | } |
| 51 | if player.isPersonalizedCommunicationRestricted { |
| 52 | disableInGameChat() |
| 53 | } |
| 54 | |
| 55 | configureAccessPoint() |
| 56 | } |
| 57 | } |
| 58 | ``` |
| 59 | |
| 60 | Guard on `GKLocalPlayer.local.isAuthenticated` before calling any GameKit API. |
| 61 | For server-side identity verification, see [references/gamekit-patterns.md](references/gamekit-patterns.md). |
| 62 | |
| 63 | ## Access Point |
| 64 | |
| 65 | `GKAccessPoint` displays a Game Center control in a corner of the screen. When |
| 66 | tapped, it opens the Game Center dashboard. Configure it after authentication. |
| 67 | |
| 68 | ```swift |
| 69 | func configureAccessPoint() { |
| 70 | GKAccessPoint.shared.location = .topLeading |
| 71 | GKAccessPoint.shared.showHighlights = true |
| 72 | GKAccessPoint.shared.isActive = true |
| 73 | } |
| 74 | ``` |
| 75 | |
| 76 | Hide the access point during gameplay and show it on menu screens: |
| 77 | |
| 78 | ```swift |
| 79 | GKAccessPoint.shared.isActive = false // Hide during active gameplay |
| 80 | GKAccessPoint.shared.isActive = true // Show on pause or menu |
| 81 | ``` |
| 82 | |
| 83 | Open the dashboard to a specific state programmatically. Specific leaderboard |
| 84 | access-point triggers require iOS 18+. |
| 85 | |
| 86 | ```swift |
| 87 | // Open directly to a leaderboard |
| 88 | GKAccessPoint.shared.trigger( |
| 89 | leaderboardID: "com.mygame.highscores", |
| 90 | playerScope: .global, |
| 91 | timeScope: .allTime |
| 92 | ) { } |
| 93 | |
| 94 | // Open directly to achievements |
| 95 | GKAccessPoint.shared.trigger(state: .achievements) { } |
| 96 | ``` |
| 97 | |
| 98 | ## Dashboard |
| 99 | |
| 100 | Present the Game Center dashboard using `GKGameCenterViewController`. The |
| 101 | presenting object must conform to `GKGameCenterControllerDelegate`. |
| 102 | |
| 103 | ```swift |
| 104 | final class GameViewController: UIViewController, GKGameCenterControllerDelegate { |
| 105 | |
| 106 | func showDashboard() { |
| 107 | let vc = GKGameCenterViewController(state: .dashboard) |
| 108 | vc.gameCenterDelegate = self |
| 109 | present(vc, animated: true) |
| 110 | } |
| 111 | |
| 112 | func showLeaderboard(_ leaderboardID: String) { |
| 113 | let vc = GKGameCenterViewController( |
| 114 | leaderboardID: leaderboardID, |
| 115 | playerScope: .global, |
| 116 | timeScope: .allTime |
| 117 | ) |
| 118 | vc.gameCenterDelegate = self |
| 119 | present(vc, animated: true) |
| 120 | } |
| 121 | |
| 122 | func gameCenterViewControllerDidFinish( |
| 123 | _ gameCenterViewController: GKGameCenterViewController |
| 124 | ) { |
| 125 | gameCenterViewController.dismiss(animated: true) |
| 126 | } |
| 127 | } |
| 128 | ``` |
| 129 | |
| 130 | Dashboard states include `.dashboard`, `.leaderboards`, `.achievements`, `.challenges`, `.localPlayerProfile`, and `.localPlayerFriendsList`. |
| 131 | |
| 132 | ## Leaderboards |
| 133 | |
| 134 | Configure leaderboards in App Store Connect before submitting scores. Supports |
| 135 | classic (persistent) and recurring (time-limited, auto-resetting) types. |
| 136 | |
| 137 | ### Submitting Scores |
| 138 | |
| 139 | Submit to one or more leaderboards using the class method: |
| 140 | |
| 141 | ```swift |
| 142 | func submitScore(_ score: Int, leaderboardIDs: [String]) async throws { |
| 143 | try await GKLeaderboard.submitScore( |
| 144 | score, |
| 145 | context: 0, |
| 146 | player: GKLocalPlayer.local, |
| 147 | leaderboardIDs: leaderboardIDs |
| 148 | ) |
| 149 | } |
| 150 | ``` |
| 151 | |
| 152 | ### Loading Entries |
| 153 | |
| 154 | ```swift |
| 155 | func loadTopScores( |
| 156 | leaderboardID: String, |
| 157 | count: Int = 10 |
| 158 | ) async throws -> (GKLeaderboard.Entry?, [GKL |