dpearson2699/swift-ios-skills3 files

Gamekit

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.

Specification
Skill ID
dpearson2699/swift-ios-skills/gamekit
Publisher
dpearson2699
Repository
swift-ios-skills
Installs
0
Files
3
Synced
Sep 23, 2026
How to use it

Open any RiverX project, open the Skills panel in the chat, and search for this identifier. The files are fetched from the source repository at install time.

dpearson2699/swift-ios-skills/gamekitInstalls these files
  • SKILL.md
  • evals/evals.json
  • references/gamekit-patterns.md

What this skill tells the agent

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.

Contents

  • Authentication
  • Access Point
  • Dashboard
  • Leaderboards
  • Achievements
  • Real-Time Multiplayer
  • Turn-Based Multiplayer
  • Common Mistakes
  • Review Checklist
  • References

Authentication

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.

func configureAccessPoint() {
    GKAccessPoint.shared.location = .topLeading
    GKAccessPoint.shared.showHighlights = true
    GKAccessPoint.shared.isActive = true
}

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.

final class GameViewController: UIViewController, GKGameCenterControllerDelegate {

    func showDashboard() {
        let vc = GKGameCenterViewController(state: .dashboard)
        vc.gameCenterDelegate = self
        present(vc, animated: true)
    }

    func showLeaderboard(_ leaderboardID: String) {
        let vc = GKGameCenterViewController(
            leaderboardID: leaderboardID,
            playerScope: .global,
            timeScope: .allTime
        )
        vc.gameCenterDelegate = self
        present(vc, animated: true)
    }

    func gameCenterViewControllerDidFinish(
        _ gameCenterViewController: GKGameCenterViewController
    ) {
        gameCenterViewController.dismiss(animated: true)
    }
}

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:

func submitScore(_ score: Int, leaderboardIDs: [String]) async throws {
    try await GKLeaderboard.submitScore(
        score,
        context: 0,
        player: GKLocalPlayer.local,
        leaderboardIDs: leaderboardIDs
    )
}

Loading Entries

func loadTopScores(
    leaderboardID: String,
    count: Int = 10
) async throws -> (GKLeaderboard.Entry?, [GKLeaderboard.Entry]) {
    let leaderboards = try await GKLeaderboard.loadLeaderboards(
        IDs: [leaderboardID]
    )
    guard let leaderboard = leaderboards.first else { return (nil, []) }

    let (localEntry, entries, _) = try await leaderboard.loadEntries(
        for: .global,
        timeScope: .allTime,
        range: 1...count
    )
    return (localEntry, entries)
}

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.

func reportAchievement(identifier: String, percentComplete: Int) async throws {
    let achievement = GKAchievement(identifier: identifier)
    achievement.percentComplete = Double(min(max(percentComplete, 0), 100))
    achievement.showsCompletionBanner = true
    try await GKAchievement.report([achievement])
}

// Unlock an achievement completely
func unlockAchievement(_ identifier: String) async throws {
    try await reportAchievement(identifier: identifier, percentComplete: 100)
}

Loading Player Achievements

func loadPlayerAchievements() async throws -> [GKAchievement] {
    try await GKAchievement.loadAchievements()
}

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:

func presentMatchmaker() {
    let request = GKMatchRequest()
    request.minPlayers = 2
    request.maxPlayers = 4
    request.inviteMessage = "Join my game!"

    guard let matchmakerVC = GKMatchmakerViewController(matchRequest: request) else {
        return
    }
    matchmakerVC.matchmakerDelegate = self
    present(matchmakerVC, animated: true)
}

Implement GKMatchmakerViewControllerDelegate:

extension GameViewController: GKMatchmakerViewControllerDelegate {
    func matchmakerViewController(
        _ viewController: GKMatchmakerViewController,
        didFind match: GKMatch
    ) {
        match.delegate = self
        viewController.dismiss(animated: true)
        startGame(with: match)
    }

    func matchmakerViewControllerWasCancelled(
        _ viewController: GKMatchmakerViewController
    ) {
        viewController.dismiss(animated: true)
    }

    func matchmakerViewController(
        _ viewController: GKMatchmakerViewController,
        didFailWithError error: Error
    ) {
        viewController.dismiss(animated: true)
    }
}

Exchanging Data

Send and receive game state through GKMatch and GKMatchDelegate:

extension GameViewController: GKMatchDelegate {
    func sendAction(_ action: GameAction, to match: GKMatch) throws {

Use this skill in a project

Open any RiverX project, search the Skills panel for dpearson2699/swift-ios-skills/gamekit, and install it in one click.

Use this skill