{"_id":"expo-gaming-services","_rev":"5-9a012afbad3e5119852b6e7f8a7b246c","name":"expo-gaming-services","dist-tags":{"latest":"0.1.3"},"versions":{"0.1.0":{"name":"expo-gaming-services","version":"0.1.0","keywords":["react-native","expo","game-center","google-play-games","leaderboards","achievements","gaming"],"author":{"name":"Nick Loyren","email":"nick@loyren.dev"},"license":"MIT","_id":"expo-gaming-services@0.1.0","maintainers":[{"name":"nick0602","email":"niccolo@loy-ren.com"}],"homepage":"https://github.com/nickloyren/expo-gaming-services#readme","bugs":{"url":"https://github.com/nickloyren/expo-gaming-services/issues"},"dist":{"shasum":"69ceff98e316c1cf4917831f50901f494a471fa2","tarball":"https://registry.npmjs.org/expo-gaming-services/-/expo-gaming-services-0.1.0.tgz","fileCount":27,"integrity":"sha512-DsmxQg/+bSdlzQxmAKxFY56hOc8wpWYeAJ4FZ2n0S+jHGWPO1eGstUSHOkFWtnRacXLMZt5Xim15Oi/9pZVwZQ==","signatures":[{"sig":"MEYCIQCazvX/BqgjavHatbbz0pSwwOh3R618EGJZojTzjQ3g0gIhAMhWkyY/VYMq31FaGciJtrrJDFw2wHMMsd1kPFN0oHmg","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":60358},"jest":{"roots":["<rootDir>/__tests__"],"testMatch":["**/__tests__/**/*.test.js"],"transform":{"^.+\\.js$":"babel-jest"},"testEnvironment":"node","testPathIgnorePatterns":["/build/"]},"main":"build/src/index.js","types":"build/src/index.d.ts","exports":{".":{"types":"./build/src/index.d.ts","default":"./build/src/index.js"},"./app.plugin":"./app.plugin.js","./package.json":"./package.json"},"gitHead":"9751a3afe060180dd4c6448c4609a63c533768fd","scripts":{"lint":"eslint src/ --ext .ts,.tsx","test":"npm run build && jest --runInBand","build":"npm run clean && expo-module build","clean":"expo-module clean","start":"expo-module start","prepare":"npm run build","typecheck":"tsc --noEmit","prepublishOnly":"npm run lint && npm test"},"_npmUser":{"name":"nick0602","email":"niccolo@loy-ren.com"},"repository":{"url":"git+https://github.com/nickloyren/expo-gaming-services.git","type":"git"},"_npmVersion":"11.13.0","description":"Expo module for Game Center (iOS) and Google Play Games (Android) - leaderboards and achievements","directories":{},"sideEffects":false,"_nodeVersion":"22.16.0","react-native":"src/index.ts","_hasShrinkwrap":false,"devDependencies":{"jest":"^29.7.0","eslint":"^8.57.0","typescript":"^5.3.0","@types/jest":"^29.5.14","expo-module-scripts":"^56.0.3","@typescript-eslint/parser":"^7.0.0","@typescript-eslint/eslint-plugin":"^7.0.0"},"peerDependencies":{"expo":"*","react":"*","react-native":"*"},"_npmOperationalInternal":{"tmp":"tmp/expo-gaming-services_0.1.0_1781809212990_0.520931195069334","host":"s3://npm-registry-packages-npm-production"}},"0.1.2":{"name":"expo-gaming-services","version":"0.1.2","keywords":["react-native","expo","game-center","google-play-games","leaderboards","achievements","gaming"],"author":{"name":"Niccolò Forlini","email":"niccolo@loy-ren.com"},"license":"MIT","_id":"expo-gaming-services@0.1.2","maintainers":[{"name":"nick0602","email":"niccolo@loy-ren.com"}],"homepage":"https://loy-ren.com","dist":{"shasum":"d8817dc2ce3de0abf08a1d5dfec0fc208ff67c8a","tarball":"https://registry.npmjs.org/expo-gaming-services/-/expo-gaming-services-0.1.2.tgz","fileCount":27,"integrity":"sha512-IxM6Tbklgvnd3QXbTRQisH/fpK7QosZL/8zO2R3nVLqUYFROqPpYj5fyWi8JEGkQUrUYqSS6clYD65HpP6MFLQ==","signatures":[{"sig":"MEYCIQCNfpAeSgtTUyxzmjvQ4LeICdhtxrHrSTDXDgJLL8Ra3QIhAJkAWJpuX2krnUGzBiuZsvewnxxC9+bSx6zgIxfkpZ+h","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":60224},"jest":{"roots":["<rootDir>/__tests__"],"testMatch":["**/__tests__/**/*.test.js"],"transform":{"^.+\\.js$":"babel-jest"},"testEnvironment":"node","testPathIgnorePatterns":["/build/"]},"main":"build/src/index.js","types":"build/src/index.d.ts","exports":{".":{"types":"./build/src/index.d.ts","default":"./build/src/index.js"},"./app.plugin":"./app.plugin.js","./package.json":"./package.json"},"gitHead":"8c06ec325a5ebfc6e49ecad098ca6131edb8a009","scripts":{"lint":"eslint src/ --ext .ts,.tsx","test":"npm run build && jest --runInBand","build":"npm run clean && expo-module build","clean":"expo-module clean","start":"expo-module start","prepare":"npm run build","typecheck":"tsc --noEmit","prepublishOnly":"npm run lint && npm test"},"_npmUser":{"name":"nick0602","email":"niccolo@loy-ren.com"},"_npmVersion":"11.13.0","description":"Expo module for Game Center (iOS) and Google Play Games (Android) - leaderboards and achievements","directories":{},"sideEffects":false,"_nodeVersion":"22.16.0","react-native":"src/index.ts","_hasShrinkwrap":false,"devDependencies":{"jest":"^29.7.0","eslint":"^8.57.0","typescript":"^5.3.0","@types/jest":"^29.5.14","expo-module-scripts":"^56.0.3","@typescript-eslint/parser":"^7.0.0","@typescript-eslint/eslint-plugin":"^7.0.0"},"peerDependencies":{"expo":"*","react":"*","react-native":"*"},"_npmOperationalInternal":{"tmp":"tmp/expo-gaming-services_0.1.2_1781809950954_0.9076461158111897","host":"s3://npm-registry-packages-npm-production"}},"0.1.3":{"name":"expo-gaming-services","version":"0.1.3","description":"Expo module for Game Center (iOS) and Google Play Games (Android) - leaderboards and achievements","main":"build/src/index.js","types":"build/src/index.d.ts","react-native":"src/index.ts","exports":{".":{"types":"./build/src/index.d.ts","default":"./build/src/index.js"},"./package.json":"./package.json","./app.plugin":"./app.plugin.js"},"sideEffects":false,"scripts":{"start":"expo-module start","build":"npm run clean && expo-module build","clean":"expo-module clean","test":"npm run build && jest --runInBand","lint":"eslint src/ --ext .ts,.tsx","prepare":"npm run build","prepublishOnly":"npm run lint && npm test","typecheck":"tsc --noEmit"},"keywords":["react-native","expo","game-center","google-play-games","leaderboards","achievements","gaming"],"homepage":"https://loy-ren.com","author":{"name":"Niccolò Forlini","email":"niccolo@loy-ren.com"},"license":"MIT","peerDependencies":{"expo":"*","react":"*","react-native":"*"},"devDependencies":{"@types/jest":"^29.5.14","expo-module-scripts":"^56.0.3","eslint":"^8.57.0","jest":"^29.7.0","typescript":"^5.3.0","@typescript-eslint/eslint-plugin":"^7.0.0","@typescript-eslint/parser":"^7.0.0"},"jest":{"testEnvironment":"node","roots":["<rootDir>/__tests__"],"testMatch":["**/__tests__/**/*.test.js"],"transform":{"^.+\\.js$":"babel-jest"},"testPathIgnorePatterns":["/build/"]},"gitHead":"5c5e32831ee829b6173f0191cdd5f33048f0885b","_id":"expo-gaming-services@0.1.3","_nodeVersion":"22.16.0","_npmVersion":"11.13.0","dist":{"integrity":"sha512-nSM2eNRx/Kr0DWbSm3aYXX3RgE/1yFQjanZxH2Qd8zjEH5BNyiC7lV539tC3lGCVFuupl9o/mrnY/pEMn3ClCw==","shasum":"d353777b1a4ad913575cb16d6d5e8ad20442441a","tarball":"https://registry.npmjs.org/expo-gaming-services/-/expo-gaming-services-0.1.3.tgz","fileCount":27,"unpackedSize":60558,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIGT2P28KoJ9oZCbPsS84/QemKV3G8nd1duHdrKXWrLEHAiB8ghrukqFkoHUcrlXvYgrGqHrDZWczhrriACbBTyi0Fw=="}]},"_npmUser":{"name":"nick0602","email":"niccolo@loy-ren.com"},"directories":{},"maintainers":[{"name":"nick0602","email":"niccolo@loy-ren.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/expo-gaming-services_0.1.3_1782714760959_0.7031235379446037"},"_hasShrinkwrap":false}},"time":{"created":"2026-06-18T19:00:12.935Z","modified":"2026-06-29T06:32:41.200Z","0.1.0":"2026-06-18T19:00:13.134Z","0.1.1":"2026-06-18T19:02:31.962Z","0.1.2":"2026-06-18T19:12:31.097Z","0.1.3":"2026-06-29T06:32:41.110Z"},"author":{"name":"Niccolò Forlini","email":"niccolo@loy-ren.com"},"license":"MIT","homepage":"https://loy-ren.com","keywords":["react-native","expo","game-center","google-play-games","leaderboards","achievements","gaming"],"description":"Expo module for Game Center (iOS) and Google Play Games (Android) - leaderboards and achievements","maintainers":[{"name":"nick0602","email":"niccolo@loy-ren.com"}],"readme":"# expo-gaming-services\n\nExpo module for **Game Center** (iOS) and **Google Play Games** (Android) - leaderboards and achievements.\n\nThis 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.\n\n## Features\n\n- ✅ Sign-in / authentication\n- ✅ Leaderboards: submit scores, fetch user score, show native UI\n- ✅ Achievements: unlock, increment progress, show native UI\n- ✅ **Per-call banner control** for achievements (iOS only)\n- ✅ Promise rejection when achievement writes fail\n- ✅ Resolves promise on UI dismiss (consistent across platforms)\n- ✅ Modern iOS scene-based view controller lookup\n- ✅ Proper error handling with Task listeners\n\n## Installation\n\n```bash\nnpx expo install expo-gaming-services\n```\n\nAdd the config plugin to your `app.json` or `app.config.js`:\n\n```json\n{\n  \"expo\": {\n    \"plugins\": [\n      [\n        \"expo-gaming-services\",\n        {\n          \"android\": {\n            \"projectId\": \"YOUR_GOOGLE_PLAY_GAMES_APP_ID\"\n          }\n        }\n      ]\n    ]\n  }\n}\n```\n\nFor iOS, there are no plugin options to set. The plugin adds the Game Center entitlement automatically.\n\nAfter adding the module and plugin, regenerate native code if needed and rebuild your app:\n\n```bash\nnpx expo prebuild\n```\n\nIf you already have native projects checked in, rebuild the app after changing the plugin config.\n\n## Prerequisites\n\nBefore testing, make sure your app is configured in the platform dashboards:\n\n- iOS: enable Game Center for your app in App Store Connect / Apple Developer and create your leaderboards and achievements there.\n- Android: create a Google Play Games Services project in Play Console and use its app ID in the plugin config.\n- Both platforms: use the exact leaderboard and achievement IDs from the platform dashboard when calling this module.\n\n## Quick Start\n\nThe normal flow is:\n\n1. Call `signIn()`.\n2. Submit or read scores and achievements.\n3. Present the native leaderboard or achievements UI when needed.\n\n```typescript\nimport GamingServices, { TimeScope } from \"expo-gaming-services\";\n\nasync function openGameServices() {\n  try {\n    const player = await GamingServices.signIn();\n    console.log(\"Signed in as:\", player.displayName);\n\n    await GamingServices.submitScore(\"leaderboard_id\", 1000);\n\n    await GamingServices.showLeaderboard(\"leaderboard_id\", {\n      timeScope: TimeScope.AllTime,\n    });\n  } catch (error) {\n    // This can happen if the user cancels sign-in or native auth cannot be shown.\n    console.log(\"Gaming services sign-in did not complete:\", error);\n  }\n}\n```\n\n## Usage\n\n```typescript\nimport GamingServices from \"expo-gaming-services\";\nimport { TimeScope } from \"expo-gaming-services\";\n\n// Authenticate\ntry {\n  const player = await GamingServices.signIn();\n  console.log(\"Signed in as:\", player.displayName);\n} catch (error) {\n  console.log(\"Game Center sign-in did not complete:\", error);\n}\n\n// Submit a score\nawait GamingServices.submitScore(\"leaderboard_id\", 1000);\n\n// Fetch current user's leaderboard entry\nconst entry = await GamingServices.loadLeaderboard(\"leaderboard_id\");\nif (entry) {\n  console.log(`Rank: ${entry.rank}, Score: ${entry.formattedScore}`);\n}\n\n// Show leaderboard UI (resolves when dismissed)\nawait GamingServices.showLeaderboard(\"leaderboard_id\", {\n  timeScope: TimeScope.AllTime,\n});\n\n// Unlock an achievement (default: no banner)\nawait GamingServices.unlockAchievement(\"achievement_id\", { showBanner: false });\n\n// Unlock with banner (iOS only)\nawait GamingServices.unlockAchievement(\"achievement_id\", { showBanner: true });\n\n// Increment achievement progress (default: no banner)\nawait GamingServices.incrementAchievement(\"achievement_id\", 10, 100, {\n  showBanner: false,\n});\n\n// Show achievements UI\nawait GamingServices.showAchievements();\n\n// Get all achievements\nconst achievements = await GamingServices.getAchievements();\nconsole.log(achievements);\n```\n\n## API Reference\n\n### Authentication\n\n- `GamingServices.signIn()` → `Player`\n- `GamingServices.isAuthenticated()` → `boolean`\n\n### Leaderboards\n\n- `GamingServices.getLeaderboards()` → `Leaderboard[]`\n- `GamingServices.submitScore(id, score, options?)` → `void`\n- `GamingServices.loadLeaderboard(id, options?)` → `LeaderboardEntry | null`\n- `GamingServices.showLeaderboard(id, options?)` → `void` (resolves on dismiss)\n\n### Achievements\n\n- `GamingServices.getAchievements()` → `Achievement[]`\n- `GamingServices.unlockAchievement(id, options?)` → `void`\n- `GamingServices.incrementAchievement(id, steps, totalSteps, options?)` → `void`\n- `GamingServices.showAchievements()` → `void` (resolves on dismiss)\n- `GamingServices.resetAchievements()` → `void` (iOS sandbox testing only)\n\n### Types\n\n```typescript\nenum TimeScope {\n  Daily,\n  Weekly,\n  AllTime,\n}\n\ninterface Player {\n  playerID: string;\n  displayName: string;\n  alias: string; // iOS only; empty string on Android\n}\n\ninterface Leaderboard {\n  id: string;\n  name: string;\n}\n\ninterface LeaderboardEntry {\n  score: number;\n  rank: number;\n  formattedScore: string;\n  context?: number;\n}\n\ninterface Achievement {\n  id: string;\n  name: string;\n  description: string;\n  unlocked: boolean;\n  unlockedAt?: number | null;\n  progress: number;\n  totalSteps: number;\n}\n```\n\n### Options\n\n#### `submitScoreOptions`\n\n- `context?: number` — Additional metadata (default: 0)\n\n#### `loadLeaderboardOptions`\n\n- `timeScope?: TimeScope` — Daily/Weekly/AllTime (default: AllTime)\n- `friendScope?: boolean` — Show friends-only or global (default: false/global)\n\n#### `unlockOptions`\n\n- `showBanner?: boolean` — Show completion banner (iOS only, default: false)\n\n## Platform Notes\n\n### iOS\n\n- Requires Game Center entitlement (automatically added by config plugin)\n- Minimum iOS version: 16.4\n- `showBanner` option controls the native Game Center completion popup\n- `signIn()` rejects if the user cancels Game Center authentication or it cannot be presented\n- Achievement writes reject with `NOT_AUTHENTICATED` if the local player is not signed in\n- Standard achievements report `progress` as `0` or `1` and `totalSteps` as `1`\n- Incremental achievements report percentage progress on a `0..100` scale because Game Center doesn't expose native step totals\n\n### Android\n\n- Requires Google Play Games App ID in config plugin\n- `unlockedAt` is `null` when unavailable (Google Play Games doesn't expose this)\n- `alias` field is always empty string (platform limitation)\n- `showBanner` option is ignored (no equivalent on Google Play Games)\n- `getLeaderboards()` returns `[]` (Play Games v2 API doesn't support fetching all leaderboards)\n- Achievement writes reject with `NOT_AUTHENTICATED` if the local player is not signed in\n- Achievement writes use the immediate APIs and reject on other failures\n- Standard achievements report `progress` as `0` or `1` and `totalSteps` as `1`\n- Incremental achievements report native Play Games step counts\n\n## Troubleshooting\n\n- Native changes are not showing up:\n  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.\n\n- `NOT_AUTHENTICATED` errors:\n  Call `signIn()` first and confirm the user completed the native sign-in flow.\n\n- Android sign-in works but leaderboards or achievements fail:\n  Double-check the Google Play Games app ID in plugin config and make sure the leaderboard or achievement IDs match Play Console exactly.\n\n- iOS sign-in prompt does not appear:\n  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.\n\n## License\n\nMIT\n","readmeFilename":"README.md"}