Skip to content
UCIK Docs
Esc
↑↓navigate↵open⌘Jpreview
On this page

Game Center services

Use Game Center achievements, leaderboards, and saved games.

Authenticate with Game Center before using these services. Create achievement and leaderboard IDs in App Store Connect; use those exact IDs in your game.

Achievements and scores

Report achievement progress
Report achievement progress
Preparing your graph…

Loading the interactive viewer.

Submit a score
Submit a score
Preparing your graph…

Loading the interactive viewer.

Read top scores
Read top scores
Preparing your graph…

Loading the interactive viewer.

Game module dependencies: GameCenterKit, BetideCore.

#include "Nodes/Achievements/GCIK_Achievements.h"
#include "Nodes/Leaderboards/GCIK_Leaderboards.h"

void ReportAppleAchievement(UObject *WorldContext, const FString &AchievementId, double Percent,
                            TFunction<void(const FBetideError &)> Completion)
{
    auto *Request =
        UGCIK_ReportAchievement::ReportAchievement(WorldContext, AchievementId, Percent, true);
    if (!Request)
        return;
    Request->OnSuccessCallback = Completion;
    Request->OnFailureCallback = MoveTemp(Completion);
    Request->Activate();
}

void SubmitAppleScore(UObject *WorldContext, const FString &LeaderboardId, int64 Score,
                      TFunction<void(const FBetideError &)> Completion)
{
    auto *Request = UGCIK_SubmitScore::SubmitScore(WorldContext, {LeaderboardId}, Score);
    if (!Request)
        return;
    Request->OnSuccessCallback = Completion;
    Request->OnFailureCallback = MoveTemp(Completion);
    Request->Activate();
}
void ReadAppleTopScores(
    UObject *WorldContext, const FString &LeaderboardId,
    TFunction<void(const TArray<FGCIK_LeaderboardEntry> &, int64, const FBetideError &)> Completion)
{
    auto *Request = UGCIK_LoadLeaderboardEntries::LoadLeaderboardEntries(
        WorldContext, LeaderboardId, EGCIK_LeaderboardPlayerScope::Global,
        EGCIK_LeaderboardTimeScope::AllTime, 1, 10);
    if (!Request)
        return;
    Request->OnSuccessCallback = Completion;
    Request->OnFailureCallback = MoveTemp(Completion);
    Request->Activate();
}

Use weak UObject captures in completion handlers. UGCIK_LeaderboardsLibrary::ShowLeaderboardUI(LeaderboardId) opens Apple’s leaderboard UI.

Achievement progress is 0–100 percent; 100 unlocks it. Leaderboard scores use the board’s configured ordering and format. Entry ranges start at 1.

Need Node / C++ entry point
Player progress Load Achievements / UGCIK_LoadAchievements::LoadAchievements
Titles and descriptions Load Achievement Descriptions / UGCIK_LoadAchievementDescriptions::LoadAchievementDescriptions
Ranked entries Load Leaderboard Entries / UGCIK_LoadLeaderboardEntries::LoadLeaderboardEntries
Entries around the player Load Player Centered Entries / UGCIK_LoadPlayerCenteredEntries::LoadPlayerCenteredEntries
Previous recurring board Load Previous Leaderboard Occurrence / UGCIK_LoadPreviousOccurrence::LoadPreviousOccurrence

For shared gameplay calls across stores, use Crossplay achievements and leaderboards. Apple’s provider supports achievements and leaderboards; it does not provide the generic Crossplay stats service.

Saved games

Game Center saves use iCloud and stay within Apple’s ecosystem. Configure iCloud for the signed app and test with an iCloud account. They are separate from Crossplay cloud saves.

Save player data
Save player data
Preparing your graph…

Loading the interactive viewer.

List saved games
List saved games
Preparing your graph…

Loading the interactive viewer.

Load saved bytes
Load saved bytes
Preparing your graph…

Loading the interactive viewer.

Resolve chosen save versions
Resolve chosen save versions
Preparing your graph…

Loading the interactive viewer.

#include "Nodes/SavedGames/GCIK_SavedGames.h"

void SaveAppleGame(UObject *WorldContext, const FString &Slot, const TArray<uint8> &Data,
                   const FScriptDelegate &OnSaved, const FScriptDelegate &OnFailed)
{
    auto *Request = UGCIK_SaveGameData::SaveGameData(WorldContext, Slot, Data);
    if (!Request)
        return;
    Request->OnSuccess.Add(OnSaved);
    Request->OnFailure.Add(OnFailed);
    Request->Activate();
}
void ListAppleSaves(UObject *WorldContext, const FScriptDelegate &OnLoaded,
                    const FScriptDelegate &OnFailed)
{
    auto *Request = UGCIK_LoadAllSavedGames::LoadAllSavedGames(WorldContext);
    if (!Request)
        return;
    Request->OnSuccess.Add(OnLoaded);
    Request->OnFailure.Add(OnFailed);
    Request->Activate();
}

void LoadAppleSave(UObject *WorldContext, const FString &Slot, const FScriptDelegate &OnLoaded,
                   const FScriptDelegate &OnFailed)
{
    auto *Request = UGCIK_LoadSavedGameData::LoadSavedGameData(WorldContext, Slot);
    if (!Request)
        return;
    Request->OnSuccess.Add(OnLoaded);
    Request->OnFailure.Add(OnFailed);
    Request->Activate();
}

void ResolveAppleSaves(UObject *WorldContext, const TArray<FGCIK_SavedGame> &Conflicts,
                       const TArray<uint8> &MergedData, const FScriptDelegate &OnResolved,
                       const FScriptDelegate &OnFailed)
{
    auto *Request = UGCIK_ResolveConflicts::ResolveConflicts(WorldContext, Conflicts, MergedData);
    if (!Request)
        return;
    Request->OnSuccess.Add(OnResolved);
    Request->OnFailure.Add(OnFailed);
    Request->Activate();
}

Bind the delegates to UFUNCTION handlers with (const FGCIK_SavedGame&, const FBetideError&). The load-data handler adds const TArray<uint8>& Data between the record and error. The list handler receives (const TArray<FGCIK_SavedGame>&, const FBetideError&); conflict resolution uses the save handler signature.

Keep a version in your save payload so later releases can read older saves. Load All Saved Games returns metadata; Load Saved Game Data reads a named save.

For conflicts, load the list first: this caches the native references required by Resolve Saved Game Conflicts. Pass the conflicting records and your chosen merged bytes. The wrapper does not merge game state for you. Its load node selects the first save with a matching name, so it cannot read each conflicting version by device or date. Only resolve with bytes your game has already chosen; do not assume this is a complete merge API. In C++, use UGCIK_ResolveConflicts::ResolveConflicts; Delete Saved Game is UGCIK_DeleteSavedGame::DeleteSavedGame.

Was this page helpful?