Overview
KalmForge.ABTests fetches every assignment for the current player, stickily caches them on disk, and lets you record conversions. The same player always gets the same variant - assignment is deterministic on the server.
- One GET per session, recorded as an exposure.
- Disk-cached at
kalmforge_ab_assignments.json. - Variant payloads are stored as raw JSON strings (parse with your own JSON library).
Quick start
1using KalmForge;2using UnityEngine;34public class OnboardingFlow : MonoBehaviour {5 async void Start() {6 await KalmForgeClient.Init();78 ABTests.OnAssignmentsLoaded += () => {9 var variant = ABTests.GetVariantKey("onboarding_v2", defaultVariant: "control");10 if (variant == "treatment") ShowNewOnboarding();11 else ShowOldOnboarding();12 };1314 await ABTests.Init();15 }1617 public async void OnFinishedTutorial() {18 await ABTests.RecordConversion("onboarding_v2");19 }20}Branching on variants
1if (ABTests.IsInExperiment("onboarding_v2")) { /* the player is enrolled */ }23if (ABTests.IsInVariant("onboarding_v2", "treatment")) { /* show new flow */ }45string key = ABTests.GetVariantKey("onboarding_v2", defaultVariant: "control");6switch (key) {7 case "control": ShowOld(); break;8 case "treatment": ShowNew(); break;9}variant_key means the player did not match the experiment's segment / rollout. defaultVariant lets you treat that as the control branch.Variant payloads
Each assignment carries an arbitrary JSON payload as a string. Use any JSON library to parse it:
1var a = ABTests.GetAssignment("price_test");2if (a != null && !string.IsNullOrEmpty(a.payload_json)) {3 // a.payload_json is e.g. {"price": 4.99, "label": "Best value"}4 var data = JsonUtility.FromJson<PricePayload>(a.payload_json);5 storeLabel.text = data.label;6}78[System.Serializable]9class PricePayload { public float price; public string label; }Remote Config overrides
If a variant defines rc_overrides, those keys win over the baseline whenever you read them via RemoteConfig - no extra code on the read site.
1// Variant rc_overrides: { "store.gem_pack_price": 4.99 }2float price = RemoteConfig.GetValue<float>("store", "gem_pack_price", 0.99f);3// → 4.99 for variant players, 0.99 for everyone else.45// Read directly if you need to know which experiment overrode a key:6if (ABTests.TryGetOverride("price_test", "store.gem_pack_price", out var raw)) {7 Debug.Log($"override = {raw}");8}910// Or scan all assigned experiments at once:11string anyOverride = ABTests.FindOverride("store.gem_pack_price");Overrides work for any Remote Config type, including structured arrays. For an array<StoryModeLevel> key, ship a JSON array of objects matching the declared field names - the SDK parses it through your [Serializable] class exactly like the baseline value.
Conversions
Call RecordConversion(experimentKey) when the player completes the goal you authored on the dashboard. The server is idempotent - you can safely call it from a retry loop.
1bool ok = await ABTests.RecordConversion("checkout_v3");Events
| Name | Type | Description |
|---|---|---|
| OnAssignmentsLoaded | event Action | Fires once after Init() resolves. |
| OnAssignmentsUpdated | event Action | Fires whenever a Fetch() merges a new server response. |
API reference
| Name | Type | Description |
|---|---|---|
| IsInitialized | bool | Init() has run. |
| IsAssignmentsLoaded | bool | OnAssignmentsLoaded has fired. |
| AssignedExperimentKeys | IEnumerable<string> | All experiments the player is currently in. |
| Init() | Task | Load cache then fetch the latest assignments. |
| Fetch(logExposure = true) | Task | Re-fetch. Pass false for silent diagnostic refreshes. |
| GetVariantKey(experimentKey, defaultVariant?) | string | Variant key or default if not enrolled. |
| IsInExperiment(experimentKey) | bool | True if the player has any variant assigned. |
| IsInVariant(experimentKey, variantKey) | bool | True if the player is in that exact variant. |
| GetAssignment(experimentKey) | Assignment | Full record (variant_key, payload_json, rc_overrides_json). |
| TryGetOverride(experimentKey, overrideKey, out value) | bool | Per-experiment override lookup. |
| FindOverride(overrideKey) | string | First override across all assigned experiments. |
| RecordConversion(experimentKey) | Task<bool> | Mark the player's exposure as converted. |
| CacheFileName | string (const) | "kalmforge_ab_assignments.json". |
REST endpoint
1# Get assignments for the player2GET /api/public/sdk/ab-tests3 ?player_id=PLAYER4 &install_id=INSTALL5 &platform=ios6 &app_version=72.0.67Headers: X-API-Key: kf_xxx_yyy89# Record a conversion10POST /api/public/sdk/ab-tests11{12 "experiment_key": "onboarding_v2",13 "player_id": "PLAYER",14 "install_id": "INSTALL"15}