{"id":"57ee55ae-3929-4112-a9c3-bba0b5ca4812","shortId":"CDFmhn","kind":"skill","title":"gamekit","tagline":"Integrate Game Center features using GameKit. Use when authenticating players with GKLocalPlayer, submitting scores to leaderboards, unlocking achievements, implementing real-time or turn-based multiplayer matchmaking, showing the Game Center access point or dashboard, or adding ","description":"# GameKit\n\nIntegrate Game Center features into iOS 26+ games using GameKit and Swift 6.3.\nProvides player authentication, leaderboards, achievements, multiplayer\nmatchmaking, access point, dashboard, challenges, and saved games.\n\n## Contents\n\n- [Authentication](#authentication)\n- [Access Point](#access-point)\n- [Dashboard](#dashboard)\n- [Leaderboards](#leaderboards)\n- [Achievements](#achievements)\n- [Real-Time Multiplayer](#real-time-multiplayer)\n- [Turn-Based Multiplayer](#turn-based-multiplayer)\n- [Common Mistakes](#common-mistakes)\n- [Review Checklist](#review-checklist)\n- [References](#references)\n\n## Authentication\n\nAll GameKit features require the local player to authenticate first. Set the\n`authenticateHandler` on `GKLocalPlayer.local` early in the app lifecycle.\nGameKit calls the handler multiple times during initialization.\n\n```swift\nimport GameKit\n\nfunc authenticatePlayer() {\n    GKLocalPlayer.local.authenticateHandler = { viewController, error in\n        if let viewController {\n            // Present so the player can sign in or create an account.\n            present(viewController, animated: true)\n            return\n        }\n        if let error {\n            // Player could not sign in. Disable Game Center features.\n            disableGameCenter()\n            return\n        }\n\n        // Player authenticated. Check restrictions before starting.\n        let player = GKLocalPlayer.local\n\n        if player.isUnderage {\n            hideExplicitContent()\n        }\n        if player.isMultiplayerGamingRestricted {\n            disableMultiplayer()\n        }\n        if player.isPersonalizedCommunicationRestricted {\n            disableInGameChat()\n        }\n\n        configureAccessPoint()\n    }\n}\n```\n\nGuard on `GKLocalPlayer.local.isAuthenticated` before calling any GameKit API.\nFor server-side identity verification, see [references/gamekit-patterns.md](references/gamekit-patterns.md).\n\n## Access Point\n\n`GKAccessPoint` displays a Game Center control in a corner of the screen. When\ntapped, it opens the Game Center dashboard. Configure it after authentication.\n\n```swift\nfunc configureAccessPoint() {\n    GKAccessPoint.shared.location = .topLeading\n    GKAccessPoint.shared.showHighlights = true\n    GKAccessPoint.shared.isActive = true\n}\n```\n\nHide the access point during gameplay and show it on menu screens:\n\n```swift\nGKAccessPoint.shared.isActive = false  // Hide during active gameplay\nGKAccessPoint.shared.isActive = true   // Show on pause or menu\n```\n\nOpen the dashboard to a specific state programmatically:\n\n```swift\n// Open directly to a leaderboard\nGKAccessPoint.shared.trigger(\n    leaderboardID: \"com.mygame.highscores\",\n    playerScope: .global,\n    timeScope: .allTime\n) { }\n\n// Open directly to achievements\nGKAccessPoint.shared.trigger(state: .achievements) { }\n```\n\n## Dashboard\n\nPresent the Game Center dashboard using `GKGameCenterViewController`. The\npresenting object must conform to `GKGameCenterControllerDelegate`.\n\n```swift\nfinal class GameViewController: UIViewController, GKGameCenterControllerDelegate {\n\n    func showDashboard() {\n        let vc = GKGameCenterViewController(state: .dashboard)\n        vc.gameCenterDelegate = self\n        present(vc, animated: true)\n    }\n\n    func showLeaderboard(_ leaderboardID: String) {\n        let vc = GKGameCenterViewController(\n            leaderboardID: leaderboardID,\n            playerScope: .global,\n            timeScope: .allTime\n        )\n        vc.gameCenterDelegate = self\n        present(vc, animated: true)\n    }\n\n    func gameCenterViewControllerDidFinish(\n        _ gameCenterViewController: GKGameCenterViewController\n    ) {\n        gameCenterViewController.dismiss(animated: true)\n    }\n}\n```\n\nDashboard states: `.dashboard`, `.leaderboards`, `.achievements`, `.localPlayerProfile`.\n\n## Leaderboards\n\nConfigure leaderboards in App Store Connect before submitting scores. Supports\nclassic (persistent) and recurring (time-limited, auto-resetting) types.\n\n### Submitting Scores\n\nSubmit to one or more leaderboards using the class method:\n\n```swift\nfunc submitScore(_ score: Int, leaderboardIDs: [String]) async throws {\n    try await GKLeaderboard.submitScore(\n        score,\n        context: 0,\n        player: GKLocalPlayer.local,\n        leaderboardIDs: leaderboardIDs\n    )\n}\n```\n\n### Loading Entries\n\n```swift\nfunc loadTopScores(\n    leaderboardID: String,\n    count: Int = 10\n) async throws -> (GKLeaderboard.Entry?, [GKLeaderboard.Entry]) {\n    let leaderboards = try await GKLeaderboard.loadLeaderboards(\n        IDs: [leaderboardID]\n    )\n    guard let leaderboard = leaderboards.first else { return (nil, []) }\n\n    let (localEntry, entries, _) = try await leaderboard.loadEntries(\n        for: .global,\n        timeScope: .allTime,\n        range: 1...count\n    )\n    return (localEntry, entries)\n}\n```\n\n`GKLeaderboard.Entry` provides `player`, `rank`, `score`, `formattedScore`,\n`context`, and `date`. For recurring leaderboard timing, leaderboard images,\nand leaderboard sets, see [references/gamekit-patterns.md](references/gamekit-patterns.md).\n\n## Achievements\n\nConfigure achievements in App Store Connect. Each achievement has a unique\nidentifier, point value, and localized title/description.\n\n### Reporting Progress\n\nSet `percentComplete` from 0.0 to 100.0. GameKit only accepts increases;\nsetting a lower value than previously reported has no effect.\n\n```swift\nfunc reportAchievement(identifier: String, percentComplete: Double) async throws {\n    let achievement = GKAchievement(identifier: identifier)\n    achievement.percentComplete = percentComplete\n    achievement.showsCompletionBanner = true\n    try await GKAchievement.report([achievement])\n}\n\n// Unlock an achievement completely\nfunc unlockAchievement(_ identifier: String) async throws {\n    try await reportAchievement(identifier: identifier, percentComplete: 100.0)\n}\n```\n\n### Loading Player Achievements\n\n```swift\nfunc loadPlayerAchievements() async throws -> [GKAchievement] {\n    try await GKAchievement.loadAchievements() ?? []\n}\n```\n\nIf an achievement is not returned, the player has no progress on it yet. Create\na new `GKAchievement(identifier:)` to begin reporting. Use\n`GKAchievement.resetAchievements()` to reset all progress during testing.\n\n## Real-Time Multiplayer\n\nReal-time multiplayer connects players in a peer-to-peer network for\nsimultaneous gameplay. Players exchange data directly through `GKMatch`.\n\n### Matchmaking with GameKit UI\n\nUse `GKMatchmakerViewController` for the standard matchmaking interface:\n\n```swift\nfunc presentMatchmaker() {\n    let request = GKMatchRequest()\n    request.minPlayers = 2\n    request.maxPlayers = 4\n    request.inviteMessage = \"Join my game!\"\n\n    guard let matchmakerVC = GKMatchmakerViewController(matchRequest: request) else {\n        return\n    }\n    matchmakerVC.matchmakerDelegate = self\n    present(matchmakerVC, animated: true)\n}\n```\n\nImplement `GKMatchmakerViewControllerDelegate`:\n\n```swift\nextension GameViewController: GKMatchmakerViewControllerDelegate {\n    func matchmakerViewController(\n        _ viewController: GKMatchmakerViewController,\n        didFind match: GKMatch\n    ) {\n        viewController.dismiss(animated: true)\n        match.delegate = self\n        startGame(with: match)\n    }\n\n    func matchmakerViewControllerWasCancelled(\n        _ viewController: GKMatchmakerViewController\n    ) {\n        viewController.dismiss(animated: true)\n    }\n\n    func matchmakerViewController(\n        _ viewController: GKMatchmakerViewController,\n        didFailWithError error: Error\n    ) {\n        viewController.dismiss(animated: true)\n    }\n}\n```\n\n### Exchanging Data\n\nSend and receive game state through `GKMatch` and `GKMatchDelegate`:\n\n```swift\nextension GameViewController: GKMatchDelegate {\n    func sendAction(_ action: GameAction, to match: GKMatch) throws {\n        let data = try JSONEncoder().encode(action)\n        try match.sendData(toAllPlayers: data, with: .reliable)\n    }\n\n    func match(_ match: GKMatch, didReceive data: Data, fromRemotePlayer player: GKPlayer) {\n        guard let action = try? JSONDecoder().decode(GameAction.self, from: data) else {\n            return\n        }\n        handleRemoteAction(action, from: player)\n    }\n\n    func match(_ match: GKMatch, player: GKPlayer, didChange state: GKPlayerConnectionState) {\n        switch state {\n        case .connected:\n            checkIfReadyToStart(match)\n        case .disconnected:\n            handlePlayerDisconnected(player)\n        default:\n            break\n        }\n    }\n}\n```\n\nData modes: `.reliable` (TCP-like, ordered, guaranteed) and `.unreliable`\n(UDP-like, faster, no guarantee). Use `.reliable` for critical game state and\n`.unreliable` for frequent position updates. Register the local player as a\nlistener (`GKLocalPlayer.local.register(self)`) to receive invitations through\n`GKInviteEventListener`. For programmatic matchmaking and custom match UI, see\n[references/gamekit-patterns.md](references/gamekit-patterns.md).\n\n## Turn-Based Multiplayer\n\nTurn-based games store match state on Game Center servers. Players take turns\nasynchronously and do not need to be online simultaneously.\n\n### Starting a Match\n\n```swift\nlet request = GKMatchRequest()\nrequest.minPlayers = 2\nrequest.maxPlayers = 4\n\nlet matchmakerVC = GKTurnBasedMatchmakerViewController(matchRequest: request)\nmatchmakerVC.turnBasedMatchmakerDelegate = self\npresent(matchmakerVC, animated: true)\n```\n\n### Taking Turns\n\nEncode game state into `Data`, end the turn, and specify the next participants:\n\n```swift\nfunc endTurn(match: GKTurnBasedMatch, gameState: GameState) async throws {\n    let data = try JSONEncoder().encode(gameState)\n\n    // Build next participants list: remaining active players\n    let nextParticipants = match.participants.filter {\n        $0.matchOutcome == .none && $0 != match.currentParticipant\n    }\n\n    try await match.endTurn(\n        withNextParticipants: nextParticipants,\n        turnTimeout: GKTurnTimeoutDefault,\n        match: data\n    )\n}\n```\n\n### Ending the Match\n\nSet outcomes for all participants, then end the match:\n\n```swift\nfunc endMatch(_ match: GKTurnBasedMatch, winnerIndex: Int, data: Data) async throws {\n    for (index, participant) in match.participants.enumerated() {\n        participant.matchOutcome = (index == winnerIndex) ? .won : .lost\n    }\n    try await match.endMatchInTurn(withMatch: data)\n}\n```\n\n### Listening for Turn Events\n\nRegister as a listener and implement `GKTurnBasedEventListener`:\n\n```swift\nGKLocalPlayer.local.register(self)\n\nextension GameViewController: GKTurnBasedEventListener {\n    func player(_ player: GKPlayer, receivedTurnEventFor match: GKTurnBasedMatch,\n                didBecomeActive: Bool) {\n        // Load match data and update UI\n        loadAndDisplayMatch(match)\n    }\n\n    func player(_ player: GKPlayer, matchEnded match: GKTurnBasedMatch) {\n        showMatchResults(match)\n    }\n}\n```\n\n### Match Data Size\n\n`matchDataMaximumSize` is 64 KB. Store larger state externally and keep only\nreferences in match data.\n\n## Common Mistakes\n\n### Not authenticating before using GameKit APIs\n\n```swift\n// DON'T\nfunc submitScore() {\n    GKLeaderboard.submitScore(100, context: 0, player: GKLocalPlayer.local,\n                              leaderboardIDs: [\"scores\"]) { _ in }\n}\n\n// DO\nfunc submitScore() async throws {\n    guard GKLocalPlayer.local.isAuthenticated else { return }\n    try await GKLeaderboard.submitScore(\n        100, context: 0, player: GKLocalPlayer.local, leaderboardIDs: [\"scores\"]\n    )\n}\n```\n\n### Setting authenticateHandler multiple times\n\n```swift\n// DON'T: Set handler on every scene transition\noverride func viewDidAppear(_ animated: Bool) {\n    super.viewDidAppear(animated)\n    GKLocalPlayer.local.authenticateHandler = { vc, error in /* ... */ }\n}\n\n// DO: Set the handler once, early in the app lifecycle\n```\n\n### Ignoring multiplayer restrictions\n\n```swift\n// DON'T\nfunc showMultiplayerMenu() { presentMatchmaker() }\n\n// DO\nfunc showMultiplayerMenu() {\n    guard !GKLocalPlayer.local.isMultiplayerGamingRestricted else { return }\n    presentMatchmaker()\n}\n```\n\n### Not setting match delegate immediately\n\n```swift\n// DON'T: Set delegate in dismiss completion -- misses early messages\nfunc matchmakerViewController(_ vc: GKMatchmakerViewController, didFind match: GKMatch) {\n    vc.dismiss(animated: true) { match.delegate = self }\n}\n\n// DO: Set delegate before dismissing\nfunc matchmakerViewController(_ vc: GKMatchmakerViewController, didFind match: GKMatch) {\n    match.delegate = self\n    vc.dismiss(animated: true)\n}\n```\n\n### Not calling finishMatchmaking for programmatic matches\n\n```swift\n// DON'T\nlet match = try await GKMatchmaker.shared().findMatch(for: request)\nstartGame(with: match)\n\n// DO\nlet match = try await GKMatchmaker.shared().findMatch(for: request)\nGKMatchmaker.shared().finishMatchmaking(for: match)\nstartGame(with: match)\n```\n\n### Not disconnecting from match\n\n```swift\n// DON'T\nfunc returnToMenu() { showMainMenu() }\n\n// DO\nfunc returnToMenu() {\n    currentMatch?.disconnect()\n    currentMatch?.delegate = nil\n    currentMatch = nil\n    showMainMenu()\n}\n```\n\n## Review Checklist\n\n- [ ] `GKLocalPlayer.local.authenticateHandler` set once at app launch\n- [ ] `isAuthenticated` checked before any GameKit API call\n- [ ] Player restrictions checked (`isUnderage`, `isMultiplayerGamingRestricted`, `isPersonalizedCommunicationRestricted`)\n- [ ] Game Center capability added in Xcode signing settings\n- [ ] Leaderboards and achievements configured in App Store Connect\n- [ ] Access point configured and toggled appropriately during gameplay\n- [ ] `GKGameCenterControllerDelegate` dismisses dashboard in `gameCenterViewControllerDidFinish`\n- [ ] Match delegate set immediately when match is found\n- [ ] `finishMatchmaking(for:)` called for programmatic matches; `disconnect()` and nil delegate on exit\n- [ ] Turn-based match data stays under 64 KB\n- [ ] Turn-based participants have outcomes set before `endMatchInTurn`\n- [ ] Invitation listener registered with `GKLocalPlayer.local.register(_:)`\n- [ ] Data mode chosen appropriately: `.reliable` for state, `.unreliable` for frequent updates\n- [ ] `NSMicrophoneUsageDescription` set if using voice chat\n- [ ] Error handling for all async GameKit calls\n\n## References\n\n- See [references/gamekit-patterns.md](references/gamekit-patterns.md) for voice chat, saved games, custom match\n  UI, leaderboard images, challenge handling, and rule-based matchmaking.\n- [GameKit documentation](https://sosumi.ai/documentation/gamekit)\n- [GKLocalPlayer](https://sosumi.ai/documentation/gamekit/gklocalplayer)\n- [GKAccessPoint](https://sosumi.ai/documentation/gamekit/gkaccesspoint)\n- [GKLeaderboard](https://sosumi.ai/documentation/gamekit/gkleaderboard)\n- [GKAchievement](https://sosumi.ai/documentation/gamekit/gkachievement)\n- [GKMatch](https://sosumi.ai/documentation/gamekit/gkmatch)\n- [GKTurnBasedMatch](https://sosumi.ai/documentation/gamekit/gkturnbasedmatch)","tags":["gamekit","swift","ios","skills","dpearson2699","accessibility","agent-skills","ai-coding","apple","claude-code","codex-skills","cursor-skills"],"capabilities":["skill","source-dpearson2699","skill-gamekit","topic-accessibility","topic-agent-skills","topic-ai-coding","topic-apple","topic-claude-code","topic-codex-skills","topic-cursor-skills","topic-ios","topic-ios-development","topic-liquid-glass","topic-localization","topic-mapkit"],"categories":["swift-ios-skills"],"synonyms":[],"warnings":[],"endpointUrl":"https://skills.sh/dpearson2699/swift-ios-skills/gamekit","protocol":"skill","transport":"skills-sh","auth":{"type":"none","details":{"cli":"npx skills add dpearson2699/swift-ios-skills","source_repo":"https://github.com/dpearson2699/swift-ios-skills","install_from":"skills.sh"}},"qualityScore":"0.684","qualityRationale":"deterministic score 0.68 from registry signals: · indexed on github topic:agent-skills · 468 github stars · SKILL.md body (14,983 chars)","verified":false,"liveness":"unknown","lastLivenessCheck":null,"agentReviews":{"count":0,"score_avg":null,"cost_usd_avg":null,"success_rate":null,"latency_p50_ms":null,"narrative_summary":null,"summary_updated_at":null},"enrichmentModel":"deterministic:skill-github:v1","enrichmentVersion":1,"enrichedAt":"2026-04-22T00:53:43.107Z","embedding":null,"createdAt":"2026-04-18T22:01:00.229Z","updatedAt":"2026-04-22T00:53:43.107Z","lastSeenAt":"2026-04-22T00:53:43.107Z","tsv":"'/documentation/gamekit)':1384 '/documentation/gamekit/gkaccesspoint)':1392 '/documentation/gamekit/gkachievement)':1400 '/documentation/gamekit/gkleaderboard)':1396 '/documentation/gamekit/gklocalplayer)':1388 '/documentation/gamekit/gkmatch)':1404 '/documentation/gamekit/gkturnbasedmatch)':1408 '0':420,938,1064,1084 '0.0':513 '0.matchoutcome':936 '1':464 '10':434 '100':1062,1082 '100.0':515,568 '2':655,882 '26':47 '4':657,884 '6.3':53 '64':1035,1319 'accept':518 'access':34,61,71,74,217,254,1279 'access-point':73 'account':161 'achiev':19,58,80,81,302,305,370,490,492,498,540,551,554,571,583,1273 'achievement.percentcomplete':544 'achievement.showscompletionbanner':546 'action':731,742,761,771 'activ':269,931 'ad':39,1266 'alltim':298,352,462 'anim':164,338,357,364,674,690,702,712,894,1105,1108,1164,1183 'api':207,1055,1255 'app':129,376,494,1121,1248,1276 'appropri':1284,1338 'async':413,435,537,560,575,918,970,1073,1356 'asynchron':865 'authent':10,56,69,70,110,119,182,242,1051 'authenticatehandl':123,1090 'authenticateplay':143 'auto':391 'auto-reset':390 'await':416,442,457,549,563,579,941,983,1080,1197,1209 'base':27,92,96,849,853,1314,1323,1378 'begin':601 'bool':1012,1106 'break':794 'build':926 'call':132,204,1186,1256,1302,1358 'capabl':1265 'case':785,789 'center':4,33,43,177,223,237,310,860,1264 'challeng':64,1373 'chat':1351,1365 'check':183,1251,1259 'checkifreadytostart':787 'checklist':104,107,1243 'chosen':1337 'class':323,404 'classic':383 'com.mygame.highscores':294 'common':98,101,1048 'common-mistak':100 'complet':555,1152 'configur':239,373,491,1274,1281 'configureaccesspoint':199,245 'conform':318 'connect':378,496,619,786,1278 'content':68 'context':419,475,1063,1083 'control':224 'corner':227 'could':171 'count':432,465 'creat':159,595 'critic':814 'currentmatch':1234,1236,1239 'custom':841,1368 'dashboard':37,63,76,77,238,280,306,311,333,366,368,1289 'data':633,715,738,746,754,755,767,795,902,921,948,968,969,986,1015,1031,1047,1316,1335 'date':477 'decod':764 'default':793 'deleg':1143,1149,1170,1237,1293,1309 'didbecomeact':1011 'didchang':780 'didfailwitherror':708 'didfind':686,1160,1177 'didrec':753 'direct':288,300,634 'disabl':175 'disablegamecent':179 'disableingamechat':198 'disablemultiplay':195 'disconnect':790,1222,1235,1306 'dismiss':1151,1172,1288 'display':220 'document':1381 'doubl':536 'earli':126,1118,1154 'effect':529 'els':450,668,768,1077,1137 'encod':741,898,924 'end':903,949,958 'endmatch':963 'endmatchinturn':1329 'endturn':913 'entri':426,455,468 'error':146,169,709,710,1111,1352 'event':990 'everi':1099 'exchang':632,714 'exit':1311 'extens':679,726,1001 'extern':1040 'fals':266 'faster':808 'featur':5,44,113,178 'final':322 'findmatch':1199,1211 'finishmatchmak':1187,1215,1300 'first':120 'formattedscor':474 'found':1299 'frequent':820,1344 'fromremoteplay':756 'func':142,244,327,340,359,407,428,531,556,573,649,682,697,704,729,749,774,912,962,1004,1021,1059,1071,1103,1129,1133,1156,1173,1228,1232 'game':3,32,42,48,67,176,222,236,309,661,719,815,854,859,899,1263,1367 'gameact':732 'gameaction.self':765 'gamecenterviewcontrol':361 'gamecenterviewcontroller.dismiss':363 'gamecenterviewcontrollerdidfinish':360,1291 'gamekit':1,7,40,50,112,131,141,206,516,639,1054,1254,1357,1380 'gameplay':257,270,630,1286 'gamest':916,917,925 'gameviewcontrol':324,680,727,1002 'gkaccesspoint':219,1389 'gkaccesspoint.shared.isactive':250,265,271 'gkaccesspoint.shared.location':246 'gkaccesspoint.shared.showhighlights':248 'gkaccesspoint.shared.trigger':292,303 'gkachiev':541,577,598,1397 'gkachievement.loadachievements':580 'gkachievement.report':550 'gkachievement.resetachievements':604 'gkgamecentercontrollerdeleg':320,326,1287 'gkgamecenterviewcontrol':313,331,346,362 'gkinviteeventlisten':836 'gkleaderboard':1393 'gkleaderboard.entry':437,438,469 'gkleaderboard.loadleaderboards':443 'gkleaderboard.submitscore':417,1061,1081 'gklocalplay':13,1385 'gklocalplayer.local':125,189,422,1066,1086 'gklocalplayer.local.authenticatehandler':144,1109,1244 'gklocalplayer.local.isauthenticated':202,1076 'gklocalplayer.local.ismultiplayergamingrestricted':1136 'gklocalplayer.local.register':830,999,1334 'gkmatch':636,688,722,735,752,777,1162,1179,1401 'gkmatchdeleg':724,728 'gkmatchmaker.shared':1198,1210,1214 'gkmatchmakerviewcontrol':642,665,685,700,707,1159,1176 'gkmatchmakerviewcontrollerdeleg':677,681 'gkmatchrequest':653,880 'gkplayer':758,779,1007,1024 'gkplayerconnectionst':782 'gkturnbasedeventlisten':997,1003 'gkturnbasedmatch':915,965,1010,1027,1405 'gkturnbasedmatchmakerviewcontrol':887 'gkturntimeoutdefault':946 'global':296,350,460 'guarante':802,810 'guard':200,446,662,759,1075,1135 'handl':1353,1374 'handleplayerdisconnect':791 'handler':134,1097,1116 'handleremoteact':770 'hide':252,267 'hideexplicitcont':192 'id':444 'ident':212 'identifi':502,533,542,543,558,565,566,599 'ignor':1123 'imag':483,1372 'immedi':1144,1295 'implement':20,676,996 'import':140 'increas':519 'index':973,978 'initi':138 'int':410,433,967 'integr':2,41 'interfac':647 'invit':834,1330 'io':46 'isauthent':1250 'ismultiplayergamingrestrict':1261 'ispersonalizedcommunicationrestrict':1262 'isunderag':1260 'join':659 'jsondecod':763 'jsonencod':740,923 'kb':1036,1320 'keep':1042 'larger':1038 'launch':1249 'leaderboard':17,57,78,79,291,369,372,374,401,440,448,480,482,485,1271,1371 'leaderboard.loadentries':458 'leaderboardid':293,342,347,348,411,423,424,430,445,1067,1087 'leaderboards.first':449 'let':149,168,187,329,344,439,447,453,539,651,663,737,760,878,885,920,933,1194,1206 'lifecycl':130,1122 'like':800,807 'limit':389 'list':929 'listen':829,987,994,1331 'load':425,569,1013 'loadanddisplaymatch':1019 'loadplayerachiev':574 'loadtopscor':429 'local':116,506,825 'localentri':454,467 'localplayerprofil':371 'lost':981 'lower':522 'match':687,696,734,750,751,775,776,788,842,856,876,914,947,951,960,964,1009,1014,1020,1026,1029,1030,1046,1142,1161,1178,1190,1195,1204,1207,1217,1220,1224,1292,1297,1305,1315,1369 'match.currentparticipant':939 'match.delegate':692,1166,1180 'match.endmatchinturn':984 'match.endturn':942 'match.participants.enumerated':976 'match.participants.filter':935 'match.senddata':744 'matchdatamaximums':1033 'matchend':1025 'matchmak':29,60,637,646,839,1379 'matchmakervc':664,673,886,893 'matchmakervc.matchmakerdelegate':670 'matchmakervc.turnbasedmatchmakerdelegate':890 'matchmakerviewcontrol':683,705,1157,1174 'matchmakerviewcontrollerwascancel':698 'matchrequest':666,888 'menu':262,277 'messag':1155 'method':405 'miss':1153 'mistak':99,102,1049 'mode':796,1336 'multipl':135,1091 'multiplay':28,59,85,89,93,97,614,618,850,1124 'must':317 'need':869 'network':627 'new':597 'next':909,927 'nextparticip':934,944 'nil':452,1238,1240,1308 'none':937 'nsmicrophoneusagedescript':1346 'object':316 'one':398 'onlin':872 'open':234,278,287,299 'order':801 'outcom':953,1326 'overrid':1102 'particip':910,928,956,974,1324 'participant.matchoutcome':977 'paus':275 'peer':624,626 'peer-to-p':623 'percentcomplet':511,535,545,567 'persist':384 'player':11,55,117,154,170,181,188,421,471,570,588,620,631,757,773,778,792,826,862,932,1005,1006,1022,1023,1065,1085,1257 'player.ismultiplayergamingrestricted':194 'player.ispersonalizedcommunicationrestricted':197 'player.isunderage':191 'playerscop':295,349 'point':35,62,72,75,218,255,503,1280 'posit':821 'present':151,162,307,315,336,355,672,892 'presentmatchmak':650,1131,1139 'previous':525 'programmat':285,838,1189,1304 'progress':509,591,608 'provid':54,470 'rang':463 'rank':472 'real':22,83,87,612,616 'real-tim':21,82,611,615 'real-time-multiplay':86 'receiv':718,833 'receivedturneventfor':1008 'recur':386,479 'refer':108,109,1044,1359 'references/gamekit-patterns.md':215,216,488,489,845,846,1361,1362 'regist':823,991,1332 'reliabl':748,797,812,1339 'remain':930 'report':508,526,602 'reportachiev':532,564 'request':652,667,879,889,1201,1213 'request.invitemessage':658 'request.maxplayers':656,883 'request.minplayers':654,881 'requir':114 'reset':392,606 'restrict':184,1125,1258 'return':166,180,451,466,586,669,769,1078,1138 'returntomenu':1229,1233 'review':103,106,1242 'review-checklist':105 'rule':1377 'rule-bas':1376 'save':66,1366 'scene':1100 'score':15,381,395,409,418,473,1068,1088 'screen':230,263 'see':214,487,844,1360 'self':335,354,671,693,831,891,1000,1167,1181 'send':716 'sendact':730 'server':210,861 'server-sid':209 'set':121,486,510,520,952,1089,1096,1114,1141,1148,1169,1245,1270,1294,1327,1347 'show':30,259,273 'showdashboard':328 'showleaderboard':341 'showmainmenu':1230,1241 'showmatchresult':1028 'showmultiplayermenu':1130,1134 'side':211 'sign':156,173,1269 'simultan':629,873 'size':1032 'skill' 'skill-gamekit' 'sosumi.ai':1383,1387,1391,1395,1399,1403,1407 'sosumi.ai/documentation/gamekit)':1382 'sosumi.ai/documentation/gamekit/gkaccesspoint)':1390 'sosumi.ai/documentation/gamekit/gkachievement)':1398 'sosumi.ai/documentation/gamekit/gkleaderboard)':1394 'sosumi.ai/documentation/gamekit/gklocalplayer)':1386 'sosumi.ai/documentation/gamekit/gkmatch)':1402 'sosumi.ai/documentation/gamekit/gkturnbasedmatch)':1406 'source-dpearson2699' 'specif':283 'specifi':907 'standard':645 'start':186,874 'startgam':694,1202,1218 'state':284,304,332,367,720,781,784,816,857,900,1039,1341 'stay':1317 'store':377,495,855,1037,1277 'string':343,412,431,534,559 'submit':14,380,394,396 'submitscor':408,1060,1072 'super.viewdidappear':1107 'support':382 'swift':52,139,243,264,286,321,406,427,530,572,648,678,725,877,911,961,998,1056,1093,1126,1145,1191,1225 'switch':783 'take':863,896 'tap':232 'tcp':799 'tcp-like':798 'test':610 'throw':414,436,538,561,576,736,919,971,1074 'time':23,84,88,136,388,481,613,617,1092 'time-limit':387 'timescop':297,351,461 'title/description':507 'toallplay':745 'toggl':1283 'topic-accessibility' 'topic-agent-skills' 'topic-ai-coding' 'topic-apple' 'topic-claude-code' 'topic-codex-skills' 'topic-cursor-skills' 'topic-ios' 'topic-ios-development' 'topic-liquid-glass' 'topic-localization' 'topic-mapkit' 'toplead':247 'transit':1101 'tri':415,441,456,548,562,578,739,743,762,922,940,982,1079,1196,1208 'true':165,249,251,272,339,358,365,547,675,691,703,713,895,1165,1184 'turn':26,91,95,848,852,864,897,905,989,1313,1322 'turn-bas':25,90,847,851,1312,1321 'turn-based-multiplay':94 'turntimeout':945 'type':393 'udp':806 'udp-lik':805 'ui':640,843,1018,1370 'uiviewcontrol':325 'uniqu':501 'unlock':18,552 'unlockachiev':557 'unreli':804,818,1342 'updat':822,1017,1345 'use':6,8,49,312,402,603,641,811,1053,1349 'valu':504,523 'vc':330,337,345,356,1110,1158,1175 'vc.dismiss':1163,1182 'vc.gamecenterdelegate':334,353 'verif':213 'viewcontrol':145,150,163,684,699,706 'viewcontroller.dismiss':689,701,711 'viewdidappear':1104 'voic':1350,1364 'winnerindex':966,979 'withmatch':985 'withnextparticip':943 'won':980 'xcode':1268 'yet':594","prices":[{"id":"9b823b89-e7b6-46f2-8b4d-b74e86428b2c","listingId":"57ee55ae-3929-4112-a9c3-bba0b5ca4812","amountUsd":"0","unit":"free","nativeCurrency":null,"nativeAmount":null,"chain":null,"payTo":null,"paymentMethod":"skill-free","isPrimary":true,"details":{"org":"dpearson2699","category":"swift-ios-skills","install_from":"skills.sh"},"createdAt":"2026-04-18T22:01:00.229Z"}],"sources":[{"listingId":"57ee55ae-3929-4112-a9c3-bba0b5ca4812","source":"github","sourceId":"dpearson2699/swift-ios-skills/gamekit","sourceUrl":"https://github.com/dpearson2699/swift-ios-skills/tree/main/skills/gamekit","isPrimary":false,"firstSeenAt":"2026-04-18T22:01:00.229Z","lastSeenAt":"2026-04-22T00:53:43.107Z"}],"details":{"listingId":"57ee55ae-3929-4112-a9c3-bba0b5ca4812","quickStartSnippet":null,"exampleRequest":null,"exampleResponse":null,"schema":null,"openapiUrl":null,"agentsTxtUrl":null,"citations":[],"useCases":[],"bestFor":[],"notFor":[],"kindDetails":{"org":"dpearson2699","slug":"gamekit","github":{"repo":"dpearson2699/swift-ios-skills","stars":468,"topics":["accessibility","agent-skills","ai-coding","apple","claude-code","codex-skills","cursor-skills","ios","ios-development","liquid-glass","localization","mapkit","networking","storekit","swift","swift-concurrency","swiftdata","swiftui","widgetkit","xcode"],"license":"other","html_url":"https://github.com/dpearson2699/swift-ios-skills","pushed_at":"2026-04-21T19:26:16Z","description":"Agent Skills for iOS 26+, Swift 6.3, SwiftUI, and modern Apple frameworks","skill_md_sha":"a6173ca172fb233adddac3a578fcb324c15160ac","skill_md_path":"skills/gamekit/SKILL.md","default_branch":"main","skill_tree_url":"https://github.com/dpearson2699/swift-ios-skills/tree/main/skills/gamekit"},"layout":"multi","source":"github","category":"swift-ios-skills","frontmatter":{"name":"gamekit","description":"Integrate Game Center features using GameKit. Use when authenticating players with GKLocalPlayer, submitting scores to leaderboards, unlocking achievements, implementing real-time or turn-based multiplayer matchmaking, showing the Game Center access point or dashboard, or adding challenges and friend invitations to iOS games."},"skills_sh_url":"https://skills.sh/dpearson2699/swift-ios-skills/gamekit"},"updatedAt":"2026-04-22T00:53:43.107Z"}}