True Count
Getting Started

Infrastructure & Environments

Dev/prod split architecture, CloudKit containers, App Store Connect setup, Superwall configuration, and release workflow for True Count.

True Count uses a dev/prod split with two separate App Store Connect app records under the same Apple Developer team. This page documents the full infrastructure: what lives where, how data flows, and how to promote a build from development to production.

Dev/Prod Split

Both apps live under the Monarch Games Apple Developer team (8D63CJZFL9). They are distinct app records with isolated catalogs, CloudKit containers, and Superwall configurations.

DevProd
App nameTrueCount DevTrueCount Blackjack
Bundle IDio.monarchgames.truecount.devio.monarchgames.truecount
App ID67705986736770598123
Xcode build configDebugRelease
PurposeTestFlight, sandbox IAP, internal QAApp Store release, real customers

Why Two App Records?

Apple scopes IAPs, Game Center leaderboards, TestFlight groups, and crash analytics per app record. Two records provide:

  • Isolated crash analytics. Dev TestFlight crashes do not pollute production metrics.
  • Isolated sandbox IAP. Dev sandbox purchases stay separate from production sales.
  • Clean promotion path. Dev builds prove the code; prod release uses the same source with Release configuration.
  • Independent TestFlight streams. Internal testers on dev, public beta on prod when needed.

Catalog duplication

Every IAP product, Superwall paywall, and CloudKit schema change must be applied to both app records. This is the tradeoff for full isolation. See the Ongoing Maintenance section below.

Xcode Build Configuration

The Xcode project uses per-configuration bundle IDs and entitlements. No manual switching is needed.

SettingDebugRelease
Bundle IDio.monarchgames.truecount.devio.monarchgames.truecount
Entitlements filetruecount-dev.entitlementstruecount.entitlements
CloudKit containeriCloud.io.monarchgames.truecount.deviCloud.io.monarchgames.truecount
DEV_BUILD flagYes (ships debug tools)No (debug tools stripped)
Superwall API keyDev application keyProd application key

Building for Debug (Xcode run, TestFlight archive) automatically targets the dev environment. Building for Release (App Store archive) automatically targets production.

CloudKit

Two Containers, Complete Isolation

Each bundle ID has its own CloudKit container. Data never crosses between them.

  • Container ID: iCloud.io.monarchgames.truecount.dev
  • Used by: Debug builds (Xcode direct install + TestFlight)
  • Private DB: SwiftData sync (game state, statistics, settings)
  • Public DB: BankrollBackup + CreditGrant records
  • Safe to reset: Yes. Dev data is throwaway.
  • Container ID: iCloud.io.monarchgames.truecount
  • Used by: Release builds (App Store)
  • Private DB: SwiftData sync (game state, statistics, settings)
  • Public DB: BankrollBackup + CreditGrant records
  • Safe to reset: No. Production user data. Schema changes are permanent.

Both containers use the same schema (cloudkit/CloudKitSchema.ckdb), deployed independently.

CloudKit Environments (per container)

Each container has two CloudKit environments internally:

EnvironmentUsed byNotes
DevelopmentXcode direct installCan be reset. Separate from Production.
ProductionTestFlight + App StorePermanent. Schema is additive-only after deployment.

TestFlight uses Production CloudKit

TestFlight builds always connect to the Production CloudKit environment within their container. Deploy the schema to the Production environment before uploading any TestFlight build.

iCloud Storage Impact

  • Each container counts against the user's iCloud quota independently.
  • The app's data footprint is tiny: a few KB per user for SwiftData private records and public DB records.
  • A user who tests via TestFlight (dev container) and uses the App Store release (prod container) will have two small data sets. This is negligible.
  • The developer's CloudKit Dashboard shows separate usage metrics per container.

Schema Deployment

The schema file at cloudkit/CloudKitSchema.ckdb is the source of truth. Deploy to both containers:

# Dev container
xcrun cktool import-schema \
  --team-id 8D63CJZFL9 \
  --container-id iCloud.io.monarchgames.truecount.dev \
  --environment development \
  --file cloudkit/CloudKitSchema.ckdb

# Prod container
xcrun cktool import-schema \
  --team-id 8D63CJZFL9 \
  --container-id iCloud.io.monarchgames.truecount \
  --environment development \
  --file cloudkit/CloudKitSchema.ckdb

Then deploy to the Production environment via CloudKit Console for each container.

In-App Purchases

All 10 IAP product IDs use the io.monarchgames.truecount.iap.* prefix:

Product IDCreditsRemoves Ads
io.monarchgames.truecount.iap.pocketaces1,500No
io.monarchgames.truecount.iap.luckydraw3,500No
io.monarchgames.truecount.iap.doubledown6,000No
io.monarchgames.truecount.iap.adfree0Yes
io.monarchgames.truecount.iap.highroller12,500Yes
io.monarchgames.truecount.iap.viptable28,000Yes
io.monarchgames.truecount.iap.thepit65,000Yes
io.monarchgames.truecount.iap.themarker105,000Yes
io.monarchgames.truecount.iap.penthousesuite190,000Yes
io.monarchgames.truecount.iap.casinovault400,000Yes

Different IDs per environment

Apple IAP product IDs are globally unique. The same string cannot exist on two apps. Dev uses io.monarchgames.truecount.dev.iap.* prefix, prod uses io.monarchgames.truecount.iap.*. StoreProductCatalog.swift selects the correct prefix based on build configuration (Debug uses dev IDs, Release uses prod IDs).

Game Center

Value
LeaderboardsAll-Time (io.monarchgames.truecount.leaderboard.alltime), Monthly (io.monarchgames.truecount.leaderboard.monthly)
Score typeNet winnings (cumulative)
SharingGame Center Group links dev and prod app records

A Game Center Group shares leaderboards between both app records. Scores submitted from dev TestFlight appear on the same leaderboards as prod. Game Center sandbox automatically isolates test scores from production scores (sandbox Game Center accounts only see sandbox data).

Superwall

Superwall only allows one iOS application per project, so dev and prod use separate Superwall projects in the same organization.

DevProd
Project ID2350319526
Bundle IDio.monarchgames.truecount.devio.monarchgames.truecount
API Keypk_NobOUD6WxX5UvSWmvF1Kxpk_suWM599AegV-fV1KJvi7Z
Product prefixio.monarchgames.truecount.dev.iap.*io.monarchgames.truecount.iap.*
Placement namecampaign_triggercampaign_trigger

SuperwallService.swift selects the correct API key at runtime based on the active build configuration. Paywalls are configured per project in the Superwall dashboard. When creating or updating a paywall, replicate changes across both projects.

Admin Panel

The admin panel (admin/ directory) manages CloudKit public database records (BankrollBackup, CreditGrant). It connects to one container at a time.

Value
FrameworkNext.js 15 + shadcn/ui
Deployed athttps://admintc.vorti.cloud
AuthenticationCloudKit server-to-server key (ECDSA P-256)

The environment switcher (Dev/Prod toggle) switches CLOUDKIT_CONTAINER between iCloud.io.monarchgames.truecount.dev and iCloud.io.monarchgames.truecount. Each container has its own server-to-server key.

Release Workflow

There is no Apple mechanism to promote a TestFlight build from one app record to another. The workflow uses the same source code with different Xcode build configurations.

Develop and test on Dev app

Build with Debug configuration. The app uses io.monarchgames.truecount.dev bundle, dev CloudKit container, dev Superwall application, and ships with DEV_BUILD debug tools.

Verify on Dev TestFlight

Upload to the dev app record. Verify sandbox IAP, Game Center, CloudKit sync, and Superwall paywall presentation on a real device.

Archive for Prod

Switch to Release configuration (automatic via Xcode archive). This builds with io.monarchgames.truecount bundle, prod CloudKit container, prod Superwall key, and strips debug tools.

Upload to Prod app

Upload to the prod app record for App Store submission or prod TestFlight beta.

Verify on Prod TestFlight

Quick smoke test before public release. Same checklist as Step 2, but against production services.

The same source code, same commit, same branch. Only the Xcode build configuration differs.

Ongoing Maintenance

When changing Apple-side catalogs, update both app records:

ChangeWhere to apply
New IAP productRegister on both dev and prod app records
IAP price changeBoth app records
New Game Center leaderboardAdd to the Game Center Group (shared automatically)
New Superwall paywallConfigure on both Superwall projects (prod 19526, dev 23503)
Superwall product catalog changeUpdate on both Superwall projects
CloudKit schema changeDeploy to both containers (dev first, verify, then prod)
App Store metadata/screenshotsProd app record only

How is this guide?

On this page