Run experiments
This page covers every SDK platform and version. A part that applies only to some platforms starts with Platform: …, and a part that applies only to some SDK versions starts with SDK version: ….
Experiments aren't available in the Godot SDK.
The justtrack SDK lets you run server-side experiments (A/B tests) in your app. Experiments are configured on the justtrack backend, which assigns each user to a variant. The SDK fetches these assignments and gives you typed access to config values so you can branch your app logic accordingly.
In this guide, you'll learn to:
- Fetch experiment assignments from the server
- Activate experiments to confirm user enrollment
- Read experiment values in your app
- Configure the fetch interval
Everything on this page works from Blueprint as well as C++. The nodes all hang off Get justtrack and live under the justtrack > Remote Config category:
| Node | What it does |
|---|---|
| Fetch Remote Config | Pulls the latest assignments from the backend. |
| Activate Remote Config | Confirms enrollment for the IDs you pass in. |
| Fetch And Activate Remote Config | Does both in one call. |
| Get All Remote Config | Reads every cached assignment. |
| Get Remote Config String | Reads one value. One node per type. |
| Set Remote Config | Changes the minimum fetch interval. |
The first three are asynchronous and take an On Complete pin. Drag from it, choose Add Custom Event, and the generated event gives you a Success boolean and an Error struct with Code and Message. Leave On Complete unbound if you don't need the result.
Fetch experiment assignments
Call fetch to pull the latest experiment assignments from the justtrack backend.
The SDK caches assignments locally, so subsequent reads are fast and offline-safe.
For example, on app launch you'd fetch the latest assignments so your UI reflects the user's assigned variants:
- Android
- iOS
- Unity
- React Native
- Flutter
- Godot
- Unreal
Platform: Android
sdk.remoteConfig.fetch().await()
Platform: iOS
try await sdk.remoteConfig.fetch()
Platform: Unity
IRemoteConfig remoteConfig = JustTrackSDK.GetRemoteConfig();
remoteConfig.Fetch(
() => { Debug.Log("Fetch succeeded"); },
(error) => { Debug.LogError("Fetch failed: " + error); }
);
Platform: React Native
await JustTrackSdk.remoteConfig.fetch();
Platform: Unreal
FJustTrackVoidResult OnComplete;
OnComplete.BindUFunction(this, GET_FUNCTION_NAME_CHECKED(AMyActor, HandleFetchComplete));
JustTrack->FetchRemoteConfig(OnComplete);
The SDK respects a minimum fetch interval to avoid excessive network requests. If you call fetch again before the interval has elapsed, the SDK returns the cached assignments without contacting the server. See Configure the fetch interval for details.
Activate experiments
Fetching assignments downloads them to the device, but doesn't tell the backend the user was actually exposed to the experiment.
Call activate to confirm enrollment — only activated experiments count toward your experiment results in the dashboard.
For example, after the user sees a new onboarding flow, activate that experiment so justtrack records the exposure:
- Android
- iOS
- Unity
- React Native
- Flutter
- Godot
- Unreal
Platform: Android
val assignments = sdk.remoteConfig.getAll()
val experimentIds = assignments?.map { it.experimentId } ?: emptyList()
sdk.remoteConfig.activate(experimentIds).await()
Platform: iOS
let assignments = sdk.remoteConfig.allAssignments
let experimentIds = assignments.map { $0.experimentId }
try await sdk.remoteConfig.activate(experimentIds: experimentIds)
Platform: Unity
IRemoteConfig remoteConfig = JustTrackSDK.GetRemoteConfig();
Assignment[] assignments = remoteConfig.GetAll();
string[] experimentIds = assignments.Select(a => a.ExperimentId).ToArray();
remoteConfig.Activate(experimentIds,
() => { Debug.Log("Activation succeeded"); },
(error) => { Debug.LogError("Activation failed: " + error); }
);
Platform: React Native
const assignments = JustTrackSdk.remoteConfig.getAll();
const experimentIds = assignments.map(a => a.experimentId);
await JustTrackSdk.remoteConfig.activate(experimentIds);
Platform: Unreal
TArray<FJustTrackRemoteConfigAssignment> Assignments;
JustTrack->GetAllRemoteConfig(Assignments);
TArray<FString> ExperimentIds;
for (const FJustTrackRemoteConfigAssignment& Assignment : Assignments)
{
ExperimentIds.AddUnique(Assignment.ExperimentId);
}
JustTrack->ActivateRemoteConfig(ExperimentIds, OnComplete);
Fetch and activate in one step
If you want to fetch the latest assignments and immediately confirm enrollment for all of them, use fetchAndActivate. This is useful at app startup when you want to get everything ready in a single call:
- Android
- iOS
- Unity
- React Native
- Flutter
- Godot
- Unreal
Platform: Android
sdk.remoteConfig.fetchAndActivate().await()
Platform: iOS
try await sdk.remoteConfig.fetchAndActivate()
Platform: Unity
IRemoteConfig remoteConfig = JustTrackSDK.GetRemoteConfig();
remoteConfig.FetchAndActivate(
() => { Debug.Log("Fetch and activate succeeded"); },
(error) => { Debug.LogError("Fetch and activate failed: " + error); }
);
Platform: React Native
await JustTrackSdk.remoteConfig.fetchAndActivate();
Platform: Unreal
JustTrack->FetchAndActivateRemoteConfig(OnComplete);
Read experiment values
After fetching, you can read experiment values from the local cache. Values are available immediately and don't require a network call.
Get all assignments
To list every experiment the user is enrolled in, call getAll. Each assignment contains the config key, its value, the experiment ID, and whether it's still pending activation.
This is useful for building a debug screen or iterating over all active experiments:
- Android
- iOS
- Unity
- React Native
- Flutter
- Godot
- Unreal
Platform: Android
val assignments = sdk.remoteConfig.getAll()
assignments?.forEach { assignment ->
Log.d("Experiment", "${assignment.configKey} = ${assignment.configValue}")
}
Platform: iOS
let assignments = sdk.remoteConfig.allAssignments
for assignment in assignments {
print("\(assignment.configKey) = \(assignment.configValue)")
}
Platform: Unity
IRemoteConfig remoteConfig = JustTrackSDK.GetRemoteConfig();
Assignment[] assignments = remoteConfig.GetAll();
foreach (Assignment assignment in assignments)
{
Debug.Log($"{assignment.ConfigKey} = {assignment.ConfigValue}");
}
Platform: React Native
const assignments = JustTrackSdk.remoteConfig.getAll();
assignments.forEach(a => {
console.log(`${a.configKey} = ${a.configValue}`);
});
Platform: Unreal
TArray<FJustTrackRemoteConfigAssignment> Assignments;
if (JustTrack->GetAllRemoteConfig(Assignments))
{
for (const FJustTrackRemoteConfigAssignment& Assignment : Assignments)
{
UE_LOG(LogTemp, Log, TEXT("%s = %s"), *Assignment.ConfigKey, *Assignment.ConfigValue);
}
}
Each assignment has the following fields:
Platform: Android, iOS, Unity, React Native, Flutter| Field | Description |
|---|---|
configKey | The key you use to look up this value (e.g. "onboarding_variant"). |
configValue | The raw string value assigned to this user. |
experimentId | The unique ID of the experiment this assignment belongs to. |
isPending | true if the assignment hasn't been activated yet. |
| Field | Description |
|---|---|
ConfigKey | The key you use to look up this value (e.g. "onboarding_variant"). |
ConfigValue | The raw string value assigned to this user. |
ExperimentId | The unique ID of the experiment this assignment belongs to. |
bPending | true if the assignment hasn't been activated yet. |
GetAllRemoteConfig returns false when no assignments are available yet, and empties the array.
In Blueprint, Get All Remote Config gives you an Assignments array pin alongside a Return Value boolean. Check Return Value first, then feed Assignments into a For Each Loop and split the struct to reach Config Key, Config Value, Experiment Id, and Pending.
Get a value by config key
To check a specific experiment value, use one of the typed getter methods. For example, to decide which onboarding flow to show:
- Android
- iOS
- Unity
- React Native
- Flutter
- Godot
- Unreal
Platform: Android
val variant = sdk.remoteConfig.getString("onboarding_variant")
if (variant == "new_flow") {
showNewOnboarding()
} else {
showDefaultOnboarding()
}
Platform: iOS
let variant = sdk.remoteConfig.getString(configKey: "onboarding_variant")
if variant == "new_flow" {
showNewOnboarding()
} else {
showDefaultOnboarding()
}
Platform: Unity
IRemoteConfig remoteConfig = JustTrackSDK.GetRemoteConfig();
string variant = remoteConfig.GetString("onboarding_variant");
if (variant == "new_flow")
{
ShowNewOnboarding();
}
else
{
ShowDefaultOnboarding();
}
Platform: React Native
const variant = JustTrackSdk.remoteConfig.getString('onboarding_variant');
if (variant === 'new_flow') {
showNewOnboarding();
} else {
showDefaultOnboarding();
}
Platform: Unreal
FString Variant;
if (JustTrack->GetRemoteConfigString(TEXT("onboarding_variant"), Variant)
&& Variant == TEXT("new_flow"))
{
ShowNewOnboarding();
}
else
{
ShowDefaultOnboarding();
}
The following typed getters are available:
Platform: Android, iOS, Unity, React Native, Flutter| Type | Android | iOS | Unity | React Native |
|---|---|---|---|---|
| String | getString() | getString() | GetString() | getString() |
| Boolean | getBoolean() | getBool() | GetBoolean() | getBoolean() |
| Integer | getInt() | getInt() | GetInt() | getNumber() |
| Double | getDouble() | getDouble() | GetDouble() | getNumber() |
| Long | getLong() | — | GetLong() | — |
All getters return null if no assignment exists for the given config key.
| Type | Function |
|---|---|
| String | GetRemoteConfigString() |
| Boolean | GetRemoteConfigBoolean() |
| Integer | GetRemoteConfigInteger() |
| Double | GetRemoteConfigDouble() |
| Long | GetRemoteConfigLong() |
Each getter writes the value to an output parameter and returns whether the key exists and holds a valid value of that type. When it returns false, the output parameter holds a zeroed default, so check the return value before you branch on it. All getters are Blueprint-pure nodes.
In Blueprint, each one takes a Config Key string and gives you two outputs: Out Value with the value itself and Return Value telling you whether it was found. Wire Return Value into a Branch before you trust Out Value, because a miss returns an empty string or a zero rather than an error.
Platform: iOSOn iOS, you can also call get(configKey:) to retrieve the full JusttrackExperimentAssignment object, which provides typed accessors like stringValue, intValue, doubleValue, and boolValue directly on the assignment.
Configure the fetch interval
The SDK enforces a minimum interval between server fetches to avoid unnecessary network requests. If you call fetch before the interval has elapsed, the SDK returns cached assignments instead.
The default interval is 24 hours (86,400 seconds).
Platform: iOS The default interval is 24 hours (86,400 seconds). Platform: React NativeThe default interval is 24 hours (86,400 seconds).
Platform: Unity The default interval is 1 hour (3,600 seconds). SDK version: 8.X.X The default interval is 1 hour (3,600 seconds). Platform: UnrealThe default interval is 1 hour (3,600 seconds).
In Blueprint, add Set Remote Config, then Make JustTrackRemoteConfigSettings and set Minimum Fetch Interval In Seconds. A negative value is rejected and the call is ignored.
During development, you can lower this interval to iterate faster:
- Android
- iOS
- Unity
- React Native
- Flutter
- Godot
- Unreal
Platform: Android
sdk.remoteConfig.setConfig(JusttrackRemoteConfigSettings(60))
Platform: iOS
sdk.remoteConfig.setConfig(JusttrackRemoteConfigSettings(minFetchIntervalInSec: 60))
Platform: Unity
IRemoteConfig remoteConfig = JustTrackSDK.GetRemoteConfig();
remoteConfig.SetConfig(new JusttrackRemoteConfigSettings(60));
Platform: React Native
JustTrackSdk.remoteConfig.setConfig({ minFetchIntervalInSeconds: 60 });
Platform: Unreal
FJustTrackRemoteConfigSettings Settings;
Settings.MinimumFetchIntervalInSeconds = 60;
JustTrack->SetRemoteConfig(Settings);
Keep the default interval in production. Lowering it causes more network requests from every user, which increases load on your app and the justtrack backend.
Example: A/B test your onboarding flow
Here's a complete example that ties everything together. Suppose you've configured an experiment on the justtrack backend with a config key onboarding_variant. Users are assigned either "control" (the existing flow) or "new_flow" (a redesigned experience).
At app startup, fetch and activate the experiment, then branch your logic based on the assigned variant:
- Android
- iOS
- Unity
- React Native
- Flutter
- Godot
- Unreal
Platform: Android
// Fetch the latest experiment assignments and activate them
sdk.remoteConfig.fetchAndActivate().await()
// Read the assigned variant
val variant = sdk.remoteConfig.getString("onboarding_variant")
when (variant) {
"new_flow" -> showNewOnboarding()
else -> showDefaultOnboarding()
}
Platform: iOS
// Fetch the latest experiment assignments and activate them
try await sdk.remoteConfig.fetchAndActivate()
// Read the assigned variant
let variant = sdk.remoteConfig.getString(configKey: "onboarding_variant")
switch variant {
case "new_flow":
showNewOnboarding()
default:
showDefaultOnboarding()
}
Platform: Unity
IRemoteConfig remoteConfig = JustTrackSDK.GetRemoteConfig();
// Fetch the latest experiment assignments and activate them
remoteConfig.FetchAndActivate(
() =>
{
// Read the assigned variant
string variant = remoteConfig.GetString("onboarding_variant");
if (variant == "new_flow")
{
ShowNewOnboarding();
}
else
{
ShowDefaultOnboarding();
}
},
(error) =>
{
Debug.LogError("Failed to fetch experiments: " + error);
// Fall back to the default experience
ShowDefaultOnboarding();
}
);
Platform: React Native
// Fetch the latest experiment assignments and activate them
await JustTrackSdk.remoteConfig.fetchAndActivate();
// Read the assigned variant
const variant = JustTrackSdk.remoteConfig.getString('onboarding_variant');
if (variant === 'new_flow') {
showNewOnboarding();
} else {
showDefaultOnboarding();
}
Platform: Unreal
void AMyActor::StartExperiments()
{
UJustTrackSubsystem* JustTrack = GetGameInstance()->GetSubsystem<UJustTrackSubsystem>();
// Fetch the latest experiment assignments and activate them
FJustTrackVoidResult OnComplete;
OnComplete.BindUFunction(this, GET_FUNCTION_NAME_CHECKED(AMyActor, HandleExperimentsReady));
JustTrack->FetchAndActivateRemoteConfig(OnComplete);
}
void AMyActor::HandleExperimentsReady(bool bSuccess, const FJustTrackError& Error)
{
if (!bSuccess)
{
UE_LOG(LogTemp, Warning, TEXT("Failed to fetch experiments: %s"), *Error.Message);
// Fall back to the default experience
ShowDefaultOnboarding();
return;
}
UJustTrackSubsystem* JustTrack = GetGameInstance()->GetSubsystem<UJustTrackSubsystem>();
// Read the assigned variant
FString Variant;
if (JustTrack->GetRemoteConfigString(TEXT("onboarding_variant"), Variant)
&& Variant == TEXT("new_flow"))
{
ShowNewOnboarding();
}
else
{
ShowDefaultOnboarding();
}
}