expo-gaming-services
v0.1.3
Published
Expo module for Game Center (iOS) and Google Play Games (Android) - leaderboards and achievements
Maintainers
Readme
expo-gaming-services
Expo module for Game Center (iOS) and Google Play Games (Android) - leaderboards and achievements.
This package focuses on native gaming identity, leaderboards, and achievements. It does not provide matchmaking, multiplayer sessions, friends APIs, cloud saves, or a custom cross-platform account system.
Features
- ✅ Sign-in / authentication
- ✅ Leaderboards: submit scores, fetch user score, show native UI
- ✅ Achievements: unlock, increment progress, show native UI
- ✅ Per-call banner control for achievements (iOS only)
- ✅ Promise rejection when achievement writes fail
- ✅ Resolves promise on UI dismiss (consistent across platforms)
- ✅ Modern iOS scene-based view controller lookup
- ✅ Proper error handling with Task listeners
Installation
npx expo install expo-gaming-servicesAdd the config plugin to your app.json or app.config.js:
{
"expo": {
"plugins": [
[
"expo-gaming-services",
{
"android": {
"projectId": "YOUR_GOOGLE_PLAY_GAMES_APP_ID"
}
}
]
]
}
}For iOS, there are no plugin options to set. The plugin adds the Game Center entitlement automatically.
After adding the module and plugin, regenerate native code if needed and rebuild your app:
npx expo prebuildIf you already have native projects checked in, rebuild the app after changing the plugin config.
Prerequisites
Before testing, make sure your app is configured in the platform dashboards:
- iOS: enable Game Center for your app in App Store Connect / Apple Developer and create your leaderboards and achievements there.
- Android: create a Google Play Games Services project in Play Console and use its app ID in the plugin config.
- Both platforms: use the exact leaderboard and achievement IDs from the platform dashboard when calling this module.
Quick Start
The normal flow is:
- Call
signIn(). - Submit or read scores and achievements.
- Present the native leaderboard or achievements UI when needed.
import GamingServices, { TimeScope } from "expo-gaming-services";
async function openGameServices() {
try {
const player = await GamingServices.signIn();
console.log("Signed in as:", player.displayName);
await GamingServices.submitScore("leaderboard_id", 1000);
await GamingServices.showLeaderboard("leaderboard_id", {
timeScope: TimeScope.AllTime,
});
} catch (error) {
// This can happen if the user cancels sign-in or native auth cannot be shown.
console.log("Gaming services sign-in did not complete:", error);
}
}Usage
import GamingServices from "expo-gaming-services";
import { TimeScope } from "expo-gaming-services";
// Authenticate
try {
const player = await GamingServices.signIn();
console.log("Signed in as:", player.displayName);
} catch (error) {
console.log("Game Center sign-in did not complete:", error);
}
// Submit a score
await GamingServices.submitScore("leaderboard_id", 1000);
// Fetch current user's leaderboard entry
const entry = await GamingServices.loadLeaderboard("leaderboard_id");
if (entry) {
console.log(`Rank: ${entry.rank}, Score: ${entry.formattedScore}`);
}
// Show leaderboard UI (resolves when dismissed)
await GamingServices.showLeaderboard("leaderboard_id", {
timeScope: TimeScope.AllTime,
});
// Unlock an achievement (default: no banner)
await GamingServices.unlockAchievement("achievement_id", { showBanner: false });
// Unlock with banner (iOS only)
await GamingServices.unlockAchievement("achievement_id", { showBanner: true });
// Increment achievement progress (default: no banner)
await GamingServices.incrementAchievement("achievement_id", 10, 100, {
showBanner: false,
});
// Show achievements UI
await GamingServices.showAchievements();
// Get all achievements
const achievements = await GamingServices.getAchievements();
console.log(achievements);API Reference
Authentication
GamingServices.signIn()→PlayerGamingServices.isAuthenticated()→boolean
Leaderboards
GamingServices.getLeaderboards()→Leaderboard[]GamingServices.submitScore(id, score, options?)→voidGamingServices.loadLeaderboard(id, options?)→LeaderboardEntry | nullGamingServices.showLeaderboard(id, options?)→void(resolves on dismiss)
Achievements
GamingServices.getAchievements()→Achievement[]GamingServices.unlockAchievement(id, options?)→voidGamingServices.incrementAchievement(id, steps, totalSteps, options?)→voidGamingServices.showAchievements()→void(resolves on dismiss)GamingServices.resetAchievements()→void(iOS sandbox testing only)
Types
enum TimeScope {
Daily,
Weekly,
AllTime,
}
interface Player {
playerID: string;
displayName: string;
alias: string; // iOS only; empty string on Android
}
interface Leaderboard {
id: string;
name: string;
}
interface LeaderboardEntry {
score: number;
rank: number;
formattedScore: string;
context?: number;
}
interface Achievement {
id: string;
name: string;
description: string;
unlocked: boolean;
unlockedAt?: number | null;
progress: number;
totalSteps: number;
}Options
submitScoreOptions
context?: number— Additional metadata (default: 0)
loadLeaderboardOptions
timeScope?: TimeScope— Daily/Weekly/AllTime (default: AllTime)friendScope?: boolean— Show friends-only or global (default: false/global)
unlockOptions
showBanner?: boolean— Show completion banner (iOS only, default: false)
Platform Notes
iOS
- Requires Game Center entitlement (automatically added by config plugin)
- Minimum iOS version: 16.4
showBanneroption controls the native Game Center completion popupsignIn()rejects if the user cancels Game Center authentication or it cannot be presented- Achievement writes reject with
NOT_AUTHENTICATEDif the local player is not signed in - Standard achievements report
progressas0or1andtotalStepsas1 - Incremental achievements report percentage progress on a
0..100scale because Game Center doesn't expose native step totals
Android
- Requires Google Play Games App ID in config plugin
unlockedAtisnullwhen unavailable (Google Play Games doesn't expose this)aliasfield is always empty string (platform limitation)showBanneroption is ignored (no equivalent on Google Play Games)getLeaderboards()returns[](Play Games v2 API doesn't support fetching all leaderboards)- Achievement writes reject with
NOT_AUTHENTICATEDif the local player is not signed in - Achievement writes use the immediate APIs and reject on other failures
- Standard achievements report
progressas0or1andtotalStepsas1 - Incremental achievements report native Play Games step counts
Troubleshooting
Native changes are not showing up: Re-run
npx expo prebuildif needed, then rebuild the native app. Config plugin changes are not applied to an already-built binary until you rebuild.NOT_AUTHENTICATEDerrors: CallsignIn()first and confirm the user completed the native sign-in flow.Android sign-in works but leaderboards or achievements fail: Double-check the Google Play Games app ID in plugin config and make sure the leaderboard or achievement IDs match Play Console exactly.
iOS sign-in prompt does not appear: Make sure Game Center is enabled for the app and test on a build where Game Center is available for the signed-in Apple account.
License
MIT
