MocaLib v2 API Reference

MocaLib v2 is a Unity 6 modular SDK wrapper library by Titipi Games.

Each module wraps a third-party SDK behind a consistent, async-first interface. Modules are individually enabled via scripting define symbols (MOCALIB_USE_<MODULE>), so only the modules you need are compiled into your build.

Installation

Add MocaLib to your project via the Unity Package Manager. Open Packages/manifest.json and add the entry under "dependencies":

{
  "dependencies": {
    "com.titipigames.unity-mocalib2": "git@titipi.github.com:TITIPI-GAMES/unity-mocalib-v2.git?path=Assets/Titipi/MocaLib#v26.7.2",
    ...
  }
}

Replace the tag (e.g. #v26.7.2) with any release tag or commit hash to pin a different version.

Enable modules

After importing, open Tools → MocaLib → Module Settings from the Unity menu bar. This opens a MocaLibConfig asset (created automatically if it doesn't exist) where you can toggle each module on or off. Scripting Define Symbols are updated automatically whenever you change a setting — no manual editing of Player Settings required.

To create a module config asset in Assets/_MocaLibConfigs/, use the corresponding Tools → MocaLib → Create <Module> Config menu item. The folder is created automatically if it doesn't exist. If the asset already exists it is selected and highlighted instead of recreated.

Firebase dependency. FirebaseInAppMessagingModule, FirestoreLeaderboardModule, and FirestorePlayerProfileModule require FirebaseModule to be enabled. Their checkboxes are grayed out in the settings UI until Firebase is turned on. Disabling Firebase automatically disables them.

The table below lists the define symbol each module uses (for reference or CI overrides):

Modules

Module Define Description
AdMobModule MOCALIB_USE_ADMOB_MODULE Google AdMob — Banner, Interstitial, Rewarded. Enable only one ad module per project.
AdjustModule MOCALIB_USE_ADJUST_MODULE Adjust — Attribution, events, ad revenue, purchase validation
AppLovinModule MOCALIB_USE_APPLOVIN_MODULE AppLovin MAX — Banner, Interstitial, Rewarded. Enable only one ad module per project.
AppsFlyerModule MOCALIB_USE_APPSFLYER_MODULE AppsFlyer — Attribution, events, ad revenue, purchase validation
ByteBrewModule MOCALIB_USE_BYTEBREW_MODULE ByteBrew — Custom events, user properties, purchases
CommonModule (always on) iOS ATT request; call before any SDK init
FacebookModule MOCALIB_USE_FACEBOOK_MODULE Facebook SDK init and app activation
FirebaseModule MOCALIB_USE_FIREBASE_MODULE Firebase — Analytics, Crashlytics, Remote Config, Push
FirebaseInAppMessagingModule MOCALIB_USE_FIREBASEINAPMESSAGING_MODULE Firebase — Native FIAM campaigns with local media caching
FirestoreLeaderboardModule MOCALIB_USE_FIREBASE_LEADERBOARD_MODULE Firestore leaderboards with server-side rank calculation
FirestorePlayerProfileModule MOCALIB_USE_FIREBASE_LEADERBOARD_MODULE Firestore-backed player profile with anonymous auth
GameAnalyticsModule MOCALIB_USE_GAMEANALYTICS_MODULE GameAnalytics — Design events, resource economy tracking
IAPModule MOCALIB_USE_IAP_MODULE Unity IAP v5 purchase flow (event-driven StoreController) with optional server validation
NetworkModule (always on) On-demand connectivity check and background polling
RatingModule MOCALIB_USE_RATING_MODULE Native in-app review / store rating prompt
ServerTimeModule MOCALIB_USE_SERVERTIME_MODULE Authoritative UTC time from Titipi server
UtilityModule (always on) Logging helpers, platform info, version, locale, IAP receipt parsing

Special Feature Flags

These are fine-grained opt-ins that augment existing modules rather than adding a new one. They appear in a separate Special section in the Module Settings UI.

Flag Define Description
CostCenter MOCALIB_USE_COST_CENTER Enables lifetime revenue tracking inside FirebaseModule — logs ad_revenue_sdk and iap_sdk events with cumulative totals stored in user properties. Requires FirebaseModule.

Initialization Order

// 1. iOS ATT — must be first, before any SDK reads IDFA
CommonModule.RequestATTracking();

// 2. Firebase — analytics, crashlytics, remote config, push; also a prerequisite
//    for FirestoreLeaderboardModule, FirestorePlayerProfileModule, and FirebaseInAppMessagingModule
await FirebaseModule.InitializeAsync(firebaseConfig);

// 3. Attribution SDKs — AppsFlyer and Adjust must follow Firebase so they can
//    share the Firebase instance ID. Start all concurrently then await each.
var fbInit = FacebookModule.InitializeAsync();
var afInit = AppsFlyerModule.InitializeAsync(appsFlyerConfig);
var adjInit = AdjustModule.InitializeAsync(adjustConfig);
await fbInit; await afInit; await adjInit;

// 4. Ad SDK — enable exactly one ad module per project (AppLovin or AdMob, not both).
//    Pass the Firebase instance ID to AppLovin for cross-platform LTV attribution.
appLovinConfig.UserId = await FirebaseModule.GetAnalyticsInstanceIdAsync();

// To receive test ads, call EnableTestAds() BEFORE InitializeAsync and populate
// TestDeviceIds in the config asset. Remove this call before shipping.
AppLovinModule.EnableTestAds(); // ← remove before release

// Fire-and-forget: mediation init does a real network round-trip and can take
// many seconds on a slow connection/device — don't block startup on it.
// UtilityModule.RunInBackground runs the work off the critical path and logs any
// exception instead of leaving it unobserved (the sanctioned place for async void).
UtilityModule.RunInBackground(async () =>
{
    await AppLovinModule.InitializeAsync(appLovinConfig);
    // — or, if using AdMob instead:
    // await AdMobModule.InitializeAsync(adMobConfig);

    // Requesting is manual — call only the formats you intend to show. Each returns
    // immediately; the ad loads asynchronously and raises OnXxxAdLoaded when ready.
    // Interstitial and Rewarded reload automatically after each show;
    // cooldown and any other pacing logic lives in the game.
    AppLovinModule.RequestBannerAd();
    AppLovinModule.RequestInterstitialAd();
    AppLovinModule.RequestRewardedAd();
});

// 5. Wire cross-module events (ad revenue → analytics, IAP → attribution)
WireEvents();

// 6. Unity IAP. Note: Ad SDK init (step 4) runs in the background and may not
//    have finished yet, so any future ad-suppression logic tied to purchases
//    must not assume the ad SDK is already initialized at this point.
await IAPModule.InitializeAsync(iapConfig, products);

// 7. Remaining SDKs — start them all first so they run concurrently, THEN await each.
//    (Awaiting one-by-one on the same line would run them sequentially — the times would add up.)
var byteBrewInit = ByteBrewModule.InitializeAsync();
var gaInit       = GameAnalyticsModule.InitializeAsync();
var ratingInit   = RatingModule.InitializeAsync(ratingConfig);
var networkInit  = NetworkModule.InitializeAsync();
var serverInit   = ServerTimeModule.InitializeAsync();
await byteBrewInit; await gaInit; await ratingInit;
await networkInit;
NetworkModule.Enable(checkInterval: 15f, timeout: 5);   // only after Network init has completed
await serverInit;

// 8. Firebase-dependent services — must follow step 2, but run concurrently with each other.
var leaderboardInit   = FirestoreLeaderboardModule.InitializeAsync();
var playerProfileInit = FirestorePlayerProfileModule.InitializeAsync();
await leaderboardInit; await playerProfileInit;

Remote Config

When FirebaseModule is enabled with UseRemoteConfig, InitializeAsync fetches and activates Remote Config before it returns — bounded by RemoteConfigFetchTimeoutSeconds (default 10s) so a slow or dead network can't stall startup. Values are therefore ready to read the moment init completes; no callback is needed at startup — just read where you use them:

var welcome  = FirebaseModule.GetRemoteString("welcome_message", "Hello!");
var maxLevel = FirebaseModule.GetRemoteInt("max_level", 100);
var flagOn   = FirebaseModule.GetRemoteBool("new_feature_enabled", false);

To pull fresh values mid-session (e.g. when the app resumes from background), call RefreshRemoteConfigAsync() and re-read — or react via the OnRemoteConfigFetched event:

// Subscribe before InitializeAsync to also catch the initial fetch.
FirebaseModule.OnRemoteConfigFetched += success => { if (success) ApplyConfig(); };

if (await FirebaseModule.RefreshRemoteConfigAsync())
    maxLevel = FirebaseModule.GetRemoteInt("max_level", 100);

Sample App

You can clone the full repo to have a working Unity Sample App that demonstrates how to use all the modules. The sample source code is placed in the Assets/_Game/Scripts folder.

A note about Async vs Callbacks

MocaLib is async-first: methods that complete once return Awaitable or Awaitable<T>. Methods that notify you of repeating events use callbacks or C# event. Knowing which pattern to reach for makes integration code simpler.

Async — "wait for one result"

Use async/await when an operation completes exactly once and the caller needs the result to proceed.

// Sequential steps read top-to-bottom — natural and easy to follow
bool rewarded = await AppLovinModule.ShowRewardedAdAsync();
if (rewarded) GiveCoins(50);

// Error handling works with plain try/catch
try
{
    var product = await IAPModule.PurchaseProductAsync("com.example.coins");
    UnlockContent(product);
}
catch (Exception e) { ShowError(e.Message); }

// Run independent operations concurrently, then collect results
var afInit  = AppsFlyerModule.InitializeAsync(config);
var adjInit = AdjustModule.InitializeAsync(config);
await afInit;
await adjInit;

Async shines when you chain several operations — no nested callbacks, no "callback hell":

// Hard to read as callbacks; trivial as async
await ShowIntroAnimationAsync();
await Awaitable.WaitForSecondsAsync(1f);
await SpawnEnemiesAsync();
StartGameplay();

Callbacks / Events — "notify me whenever"

Use callbacks or C# events for things that can happen zero, one, or many times — subscribing once and staying subscribed.

// Module-level events: wire once, receive every occurrence
IAPModule.OnPurchaseSucceeded += (product, receipt) =>
    AppsFlyerModule.LogPurchase(product, receipt);

AppLovinModule.OnAdRevenuePaid += data =>
    FirebaseModule.LogAdRevenue(data, "AppLovin");

// Optional outcome callbacks on longer-lived operations
FirebaseModule.RegisterForPushNotifications(
    onGranted: () => ShowPushEnabledBadge(),
    onDenied:  () => ShowPushHint()
);

Quick reference

Pattern Use when… MocaLib examples
await Operation completes once; caller needs the result to continue InitializeAsync, ShowRewardedAdAsync, PurchaseProductAsync, SaveProfileAsync
Callback / event Event fires repeatedly or you need multiple subscribers OnPurchaseSucceeded, OnAdRevenuePaid, RegisterForPushNotifications

Rule of thumb: "I need a result" → async. "Tell me every time this happens" → callback or event.

Conventions the modules follow

These are the rules every MocaLib module honors, so callers can rely on them:

Init returns success as a value, not an exception. Every InitializeAsync returns Awaitable<bool>: true on success, false on an expected failure (missing config, unresolved dependencies, unsupported platform). The failure reason is logged internally via UtilityModule.MocaLibLogError, so you don't need a try/catch around init just to learn why it failed — check the bool if the result gates your next step, ignore it if it doesn't.

if (!await FirebaseModule.InitializeAsync(cfg))
    Debug.LogError("Firebase failed — leaderboard/profile/FIAM unavailable.");

This is deliberately not a callback and not a result struct — bool + internal logging gives you explicit, value-typed success/failure while keeping linear await sequencing and single-site error handling. Reach for try/catch only for genuinely unexpected exceptions.

Events fire on the Unity main thread. All module events (OnAdRevenuePaid, OnInterstitialAdLoaded, OnPurchaseSucceeded, …) are raised on the main thread, so your handlers can touch Unity objects and other SDKs directly — no manual thread marshalling. (The ad modules do this internally where their SDKs raise events off-thread; e.g. AdMob marshals every callback through MobileAdsEventExecutor.ExecuteInUpdate.)

Fire-and-forget goes through UtilityModule.RunInBackground. When you want to start async work but not block the caller — e.g. warming up the ad SDK during startup so it doesn't hold up the loading screen — wrap it in RunInBackground. It runs the work off the critical path and logs any exception instead of leaving it unobserved. It is the one sanctioned place for async void in MocaLib; don't write your own.

UtilityModule.RunInBackground(async () =>
{
    await AdMobModule.InitializeAsync(adMobConfig);
    AdMobModule.RequestBannerAd();
});

Parallelize init, but await before finishing the loading screen. Start independent SDKs concurrently (kick them all off, then await each) so their init times overlap instead of adding up — but do let them finish before you leave the loading screen, so everything is ready when the first screen appears. Respect real dependencies: Firebase before the ad user-id and Firestore modules, attribution SDKs after Firebase. The one exception is the ad SDK, which goes through RunInBackground: its init plus async ad fill can take several seconds and nothing on the first screen needs it, so blocking on it would only pad the loading screen for no benefit.