Skip to main content

Experiments

Run A/B tests to optimize pricing, paywalls, onboarding flows, and feature adoption. AppActor handles user assignment, targeting, and variant delivery.

How It Worksโ€‹

  1. Create an experiment on the server with variants and targeting rules
  2. Request assignment from the SDK โ€” the server assigns the user to a variant
  3. Use the variant to show different UI or behavior
  4. Track goals to measure which variant performs better

Getting a Variantโ€‹

if let assignment = try await AppActor.shared.getExperimentAssignment(
experimentKey: "paywall_v2"
) {
// Assignment exists โ†’ user is in experiment
switch assignment.variantKey {
case "control":
showOriginalPaywall()
case "variant_a":
showNewPaywall()
default:
showOriginalPaywall()
}

// Optional payload access
if let showDiscount = assignment.payload["showDiscount"]?.boolValue, showDiscount {
showDiscountBadge()
}
} else {
// User not targeted or experiment not running
showOriginalPaywall()
}

Experiment Structureโ€‹

Key Conceptsโ€‹

ConceptDescription
Experiment KeyUnique identifier (e.g., "paywall_v2")
Traffic AllocationPercentage of eligible users included (in basis points, 10000 = 100%)
TargetingRules to filter eligible users (same as Remote Config)
VariantA named group with a weight and payload
ControlThe baseline variant (usually current behavior)
WeightPercentage of experiment traffic for this variant (in basis points)
PayloadArbitrary JSON data attached to the variant
Sticky AssignmentOnce assigned, a user always sees the same variant

REST APIโ€‹

Request Assignmentโ€‹

POST /v1/experiments/paywall_v2/assignments?app_user_id=user_123
Authorization: Bearer pk_live_your_key_here

Query Parameters:

ParameterRequiredDescription
app_user_idYesUser ID (max 256 characters)
app_versionNoApp version for targeting
countryNoISO 3166-1 alpha-2 code for targeting

Response (Assigned)โ€‹

{
"data": {
"experiment": {
"id": "exp_abc123",
"key": "paywall_v2"
},
"variant": {
"id": "var_xyz789",
"key": "variant_a",
"valueType": "json",
"payload": {
"layout": "new",
"showDiscount": true
}
},
"inExperiment": true,
"assignedAt": "2025-01-15T12:00:00.000Z"
},
"requestId": "req_abc123"
}

Response (Not Targeted)โ€‹

{
"data": {
"inExperiment": false,
"reason": "not_targeted"
},
"requestId": "req_abc123"
}

Targeting Conditionsโ€‹

Same targeting system as Remote Config:

ConditionOperatorsExample
Platformeq, neq, in, not_iniOS only
App Versioneq, gt, gte, lt, ltev2.0.0+
Countryeq, neq, in, not_inUS and CA
Entitlementhas, not_hasFree users only

Goalsโ€‹

Track what you're optimizing for:

Goal TypeDescription
conversionDid the user convert (e.g., subscribe)?
revenueRevenue generated
retentionUser retention after N days
customCustom event tracking

Best Practicesโ€‹

  1. Always handle the "not in experiment" case โ€” Show default behavior when the user isn't targeted
  2. Use the control variant โ€” Always have a control group for comparison
  3. Check assignment early โ€” Fetch the assignment before rendering the UI
  4. Don't change experiments mid-flight โ€” Let experiments run to completion for valid results

Next Stepsโ€‹