npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

expo-gaming-services

v0.1.3

Published

Expo module for Game Center (iOS) and Google Play Games (Android) - leaderboards and achievements

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-services

Add 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 prebuild

If 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:

  1. Call signIn().
  2. Submit or read scores and achievements.
  3. 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()Player
  • GamingServices.isAuthenticated()boolean

Leaderboards

  • GamingServices.getLeaderboards()Leaderboard[]
  • GamingServices.submitScore(id, score, options?)void
  • GamingServices.loadLeaderboard(id, options?)LeaderboardEntry | null
  • GamingServices.showLeaderboard(id, options?)void (resolves on dismiss)

Achievements

  • GamingServices.getAchievements()Achievement[]
  • GamingServices.unlockAchievement(id, options?)void
  • GamingServices.incrementAchievement(id, steps, totalSteps, options?)void
  • GamingServices.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
  • showBanner option controls the native Game Center completion popup
  • signIn() rejects if the user cancels Game Center authentication or it cannot be presented
  • Achievement writes reject with NOT_AUTHENTICATED if the local player is not signed in
  • Standard achievements report progress as 0 or 1 and totalSteps as 1
  • Incremental achievements report percentage progress on a 0..100 scale because Game Center doesn't expose native step totals

Android

  • Requires Google Play Games App ID in config plugin
  • unlockedAt is null when unavailable (Google Play Games doesn't expose this)
  • alias field is always empty string (platform limitation)
  • showBanner option 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_AUTHENTICATED if the local player is not signed in
  • Achievement writes use the immediate APIs and reject on other failures
  • Standard achievements report progress as 0 or 1 and totalSteps as 1
  • Incremental achievements report native Play Games step counts

Troubleshooting

  • Native changes are not showing up: Re-run npx expo prebuild if needed, then rebuild the native app. Config plugin changes are not applied to an already-built binary until you rebuild.

  • NOT_AUTHENTICATED errors: Call signIn() 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