Unity SDK

A/B Tests

Server-assigned, sticky variant assignment per (experiment, player). Variants can carry a free-form JSON payload and/or a set of Remote Config key overrides that automatically take precedence on read.

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

OnboardingFlow.csC#
1using KalmForge;
2using UnityEngine;
3
4public class OnboardingFlow : MonoBehaviour {
5 async void Start() {
6 await KalmForgeClient.Init();
7
8 ABTests.OnAssignmentsLoaded += () => {
9 var variant = ABTests.GetVariantKey("onboarding_v2", defaultVariant: "control");
10 if (variant == "treatment") ShowNewOnboarding();
11 else ShowOldOnboarding();
12 };
13
14 await ABTests.Init();
15 }
16
17 public async void OnFinishedTutorial() {
18 await ABTests.RecordConversion("onboarding_v2");
19 }
20}

Branching on variants

exampleC#
1if (ABTests.IsInExperiment("onboarding_v2")) { /* the player is enrolled */ }
2
3if (ABTests.IsInVariant("onboarding_v2", "treatment")) { /* show new flow */ }
4
5string key = ABTests.GetVariantKey("onboarding_v2", defaultVariant: "control");
6switch (key) {
7 case "control": ShowOld(); break;
8 case "treatment": ShowNew(); break;
9}
Note
A null/empty 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:

exampleC#
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}
7
8[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.

exampleC#
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.
4
5// 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}
9
10// 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.

Heads up
Don't ship two simultaneous experiments that override the same Remote Config key - the winner is unspecified.

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.

exampleC#
1bool ok = await ABTests.RecordConversion("checkout_v3");

Events

NameTypeDescription
OnAssignmentsLoadedevent ActionFires once after Init() resolves.
OnAssignmentsUpdatedevent ActionFires whenever a Fetch() merges a new server response.

API reference

ABTests - static API
NameTypeDescription
IsInitializedboolInit() has run.
IsAssignmentsLoadedboolOnAssignmentsLoaded has fired.
AssignedExperimentKeysIEnumerable<string>All experiments the player is currently in.
Init()TaskLoad cache then fetch the latest assignments.
Fetch(logExposure = true)TaskRe-fetch. Pass false for silent diagnostic refreshes.
GetVariantKey(experimentKey, defaultVariant?)stringVariant key or default if not enrolled.
IsInExperiment(experimentKey)boolTrue if the player has any variant assigned.
IsInVariant(experimentKey, variantKey)boolTrue if the player is in that exact variant.
GetAssignment(experimentKey)AssignmentFull record (variant_key, payload_json, rc_overrides_json).
TryGetOverride(experimentKey, overrideKey, out value)boolPer-experiment override lookup.
FindOverride(overrideKey)stringFirst override across all assigned experiments.
RecordConversion(experimentKey)Task<bool>Mark the player's exposure as converted.
CacheFileNamestring (const)"kalmforge_ab_assignments.json".

REST endpoint

examplebash
1# Get assignments for the player
2GET /api/public/sdk/ab-tests
3 ?player_id=PLAYER
4 &install_id=INSTALL
5 &platform=ios
6 &app_version=72.0.6
7Headers: X-API-Key: kf_xxx_yyy
8
9# Record a conversion
10POST /api/public/sdk/ab-tests
11{
12 "experiment_key": "onboarding_v2",
13 "player_id": "PLAYER",
14 "install_id": "INSTALL"
15}
Back to DocsKalmForge SDK · v1.0.1