Variables

You can create variables that can take on new values from the server. This allows you to roll out changes without having to push an update through the App Store or Google Play. You can also create A/B tests to change variables for only a percentage of your users.

Variable data comes back asynchronously after you call start. If you need to use a variable when the app starts, make sure you use callbacks.

Defining variables

When you define a variable in your code, it will appear on the Variables tab the next time your app starts in development mode.

Objective-C

Define variables outside of your methods, like you would a constant, using our DEFINE_VAR macros. Use underscores in the name to group values into a structured variable. See modeling structured data.

Swift

Swift doesn't support macros like Objective-C. Instead, define variables using LPVar.define directly. Use dots in the name to group values into a structured variable. See modeling structured data.

With Swift, you must define all of your Leanplum variables before calling Leanplum.start.

To access a variable in another controller or file, call define again; the first define call (before start) will set the value, subsequent calls will return the cached value. Be careful where you place define calls to ensure the first call is truly called first.

Variable types

Float

Set the value with the macro DEFINE_VAR_FLOAT in ObjC, or LPVar.define in Swift. To access the value in your code, use the floatValue method.

DEFINE_VAR_FLOAT(shootSpeed, 1.0);  // How fast your ship shoots.
...
[Leanplum onVariablesChanged:^() {
  // Move ship according to its speed.
  [myShip moveWithSpeed:shootSpeed.floatValue];
}];
var shootSpeed = LPVar.define("shootSpeed", with:1.0); // How fast your ship shoots.
...
Leanplum.onVariablesChanged {
  // Move ship according to its speed.
  myShip.moveWithSpeed(shootSpeed?.floatValue())
}

Boolean

Set the value with the macro DEFINE_VAR_BOOL in ObjC, or LPVar.define in Swift. To access the value in your code, use the boolValue method.

DEFINE_VAR_BOOL(showAds, false);  // Whether or not to show ads in the app.
...
[Leanplum onVariablesChanged:^() {
  if (showAds.boolValue) {
    [self.view addSubview:adView];
  }
}];
var showAds = LPVar.define("showAds", with: false) // Whether or not to show ads in the app.
...
Leanplum.onVariablesChanged {
  if(showAds?.boolValue())! {
    self.view.addSubview(adView)
  }
}

String

Set the value with the macro DEFINE_VAR_STRING in ObjC, or LPVar.define in Swift. To access the value in your code, use the stringValue method.

DEFINE_VAR_STRING(startLabel, @"Start");  // Label of the "Start" button.
...
[Leanplum onVariablesChanged:^() {
  UIButton* startButton = [[UIButton alloc] init];
  startButton.text = startLabel.stringValue;
  [self.view addSubview:startButton];
}];
var startLabel = LPVar.define("startLabel", with: "Start") // Label of the "Start" button
...
Leanplum.onVariablesChanged {
  let startButton: UIButton = UIButton()
  startButton.setTitle(startLabel?.stringValue(), for: .normal)
  self.view.addSubview(startButton)
}

Color

Set the value with the macro DEFINE_VAR_COLOR in ObjC, or LPVar.define in Swift. To access the value in your code, use the colorValue method.

DEFINE_VAR_COLOR(myColor, [UIColor colorWithRed:14.0/255.0 green:114.0/255.0 blue:199.0/255.0 alpha:1]);
...
[Leanplum onVariablesChanged:^() {
  [startButton setTitleColor:myColor.colorValue];
}];
var myColor = LPVar.define("myColor", with: UIColor.gray)
...
Leanplum.onVariablesChanged {
  startButton.setTitleColor(myColor?.colorValue(), for: .normal)
}

Assets

File variables work like variables except that the file data comes back from Leanplum separately. When you edit a file variable, you change its filename. If a file with the same name does not exist on the device, it will download from Leanplum.

Set the value with the macro DEFINE_VAR_FILE in ObjC, or LPVar.define using a withFile parameter in Swift. To access the value in your code, use the fileValue or imageValue method.

// Image and file.
DEFINE_VAR_FILE(goldStar, @"gold_star.png");  // Location of Gold Star image file.
DEFINE_VAR_FILE(config, @"config.plist");
...
[Leanplum onVariablesChanged:^() {
  // imageValue is compatible with Asset Catalogs.
  self.splashView.image = goldStar.imageValue;
  NSDictionary* config = [NSDictionary dictionaryWithContentsOfFile:config.fileValue];
}];
// Image
var goldStar = LPVar.define("goldStar", withFile: "gold_star.png") // Location of Gold Star image file.
...
Leanplum.onVariablesChanged {
  self.splashView?.image = goldStar?.imageValue()
}

Dictionary

Set the value with the macro DEFINE_VAR_DICTIONARY_WITH_OBJECTS_AND_KEYS in ObjC, or LPVar.define in Swift. To access a dictionary value in your code, use the objectForKey method to get to the correct property, then use an accessor method (stringValue, boolValue, floatValue, etc.) to get the actual value. In Swift, you may need to cast after using objectForKey.

DEFINE_VAR_DICTIONARY_WITH_OBJECTS_AND_KEYS(
  powerUp,
  @"Turbo Boost", @"name",
  @150, @"price",
  @1.5, @"speedMultiplier",
  @15, @"timeout",
  nil);
...
[Leanplum onVariablesChanged:^() {
  self.speed *= [[powerUp objectForKey:@"speedMultiplier"] floatValue];
}];
let powerUp = LPVar.define("powerUp", with: [
  "name": "Turbo Boost",
  "price": 150,
  "speedMultiplier": 1.5,
  "timeout": 15])
...
Leanplum.onVariablesChanged {
  self.speed = (powerUp?.object(forKey: "speedMultiplier") as! NSNumber).floatValue
}

Array

Set the value with the macro DEFINE_VAR_ARRAY_WITH_OBJECTS in ObjC, or LPVar.define in Swift. To access a dictionary value in your code, use the objectAtIndex method to get to the correct object, then use an accessor method (stringValue, boolValue, floatValue, etc.) to get the actual value. In Swift, you may need to cast after using objectAtIndex.

DEFINE_VAR_ARRAY_WITH_OBJECTS(storeItemsOrder, @0, @1, @2, @3, @4, nil);
...
[Leanplum onVariablesChanged:^(){
  for (int i = 0; i < storeItemsOrder.count; i++) {
    int item = [[storeItemsOrder objectAtIndex:i] intValue];
    NSLog(@"%i", item);
  }
}];
let storeItemsOrder = LPVar.define("storeItemsOrder", with: [0, 1, 2, 3, 4])
Leanplum.onVariablesChanged {
  for var i in 0..<storeItemsOrder!.count(){
    let item = storeItemsOrder!.object(at: i) as! NSNumber
    print(item.intValue)
    i += 1
  }
}

Modeling structured data

Say you have a bunch of items in your app, and each item has properties. Leanplum gives you the flexibility to model one property of the object at a time, one object at a time, or the entire structure at once, depending on how you'd like to set up your code. For example, if you want to create a structure like this:

Powerups: {
    Speed: {
        Price: 10,
        Duration: 5,
        OrderInStore: 0
    },
    Power: {
        Price: 15,
        Duration: 5,
        OrderInStore: 1
    }
}

You could model each individual field like so:

// Names or groups with an '_' are grouped automatically.
DEFINE_VAR_FLOAT(Powerups_Speed_Price, 10);
DEFINE_VAR_FLOAT(Powerups_Speed_Duration, 5);
DEFINE_VAR_FLOAT(Powerups_Speed_OrderInStore, 0);

DEFINE_VAR_FLOAT(Powerups_Power_Price, 15);
DEFINE_VAR_FLOAT(Powerups_Power_Duration, 5);
DEFINE_VAR_FLOAT(Powerups_Power_OrderInStore, 1);
// Names or groups with a '.' are grouped automatically.
var speedPrice = LPVar.define("Powerups.Speed.Price", with:10)
var speedDuration = LPVar.define("Powerups.Speed.Duration", with:5)
var speedOrder = LPVar.define("Powerups.Speed.OrderInStore", with:0)

var powerPrice = LPVar.define("Powerups.Power.Price", with:15)
var powerDuration = LPVar.define("Powerups.Power.Duration", with:5)
var powerOrder = LPVar.define("Powerups.Power.OrderInStore", with:1)
// Names or groups with a '.' are grouped automatically.
@Variable(name="Powerups.Speed.Price") double speedPrice = 10;
@Variable(name="Powerups.Speed.Duration") double speedDuration = 5;
@Variable(name="Powerups.Speed.OrderInStore") int speedOrder = 0;

@Variable(name="Powerups.Power.Price") double powerPrice = 15;
@Variable(name="Powerups.Power.Duration") double powerDuration = 5;
@Variable(name="Powerups.Power.OrderInStore") int powerOrder = 1;
// Names with a '.' are grouped automatically.
Var<int> speedPrice = Var<int>.Define("Powerups.Speed.Price", 10);
Var<int> speedDuration = Var<int>.Define("Powerups.Speed.Duration", 5);
Var<int> speedOrder = Var<int>.Define("Powerups.Speed.Order in Store", 0);

Var<int> powerPrice = Var<int>.Define("Powerups.Power.Price", 15);
Var<int> powerDuration = Var<int>.Define("Powerups.Power.Duration", 5);
Var<int> powerOrder = Var<int>.Define("Powerups.Power.Order in Store", 1);

Or you could model each object:

DEFINE_VAR_DICTIONARY_WITH_OBJECTS_AND_KEYS(Powerups_Speed, @10, @"Price", @5, @"Duration", @0, @"OrderInStore", nil);
DEFINE_VAR_DICTIONARY_WITH_OBJECTS_AND_KEYS(Powerups_Power, @15, @"Price", @5, @"Duration", @1, @"OrderInStore", nil);
var powerUpsSpeed = LPVar.define("Powerups.Speed", with: [
  "Price": 10,
  "Duration": 5,
  "OrderInStore": 0
])
var powerUpsPower = LPVar.define("Powerups.Power", with: [
  "Price": 15,
  "Duration": 5,
  "OrderInStore": 1
])
// Using the Google Guava library for brevity.
@Variable(group="Powerups") Map<String, Object> speed =
    ImmutableMap.of("Price", 10.0, "Duration", 5.0, "OrderInStore", 0);
@Variable(group="Powerups") Map<String, Object> duration =
    ImmutableMap.of("Price", 15.0, "Duration", 5.0, "OrderInStore", 1);
Var<Dictionary<string, object>> speed = Var<Dictionary<string, object>>.Define(
    "Powerups.Speed", new Dictionary<string, object>>() {
        { "Price", 10 },
        { "Duration", 5 },
        { "OrderInStore", 0 } });
Var<Dictionary<string, object>> power = Var<Dictionary<string, object>>.Define(
    "Powerups.Power", new Dictionary<string, object>>() {
        { "Price", 15 },
        { "Duration", 5 },
        { "OrderInStore", 1 } });

Or the entire structure:

LPVar* powerups;
static void __attribute__((constructor)) initObjects() {
    @autoreleasepool {
        powerups = [LPVar define:@"Powerups" withDictionary:@{
            @"Speed": @{@"Price" : @10, @"Duration": @5, @"OrderInStore": 0},
            @"Power": @{@"Price" : @15, @"Duration": @5, @"OrderInStore": 1}
        }];
    }
}
var powerups = LPVar.define("Powerups", with: [
  "Speed": [
    "Price": 10,
    "Duration": 5,
    "OrderInStore": 0
  ],
  "Power": [
    "Price": 15,
    "Duration": 5,
    "OrderInStore": 1
  ]
])
@Variable Map<String, Object> powerups = ImmutableMap.of(
    "Speed", ImmutableMap.of("Price", 10.0, "Duration", 5.0, "OrderInStore", 0),
    "Power", ImmutableMap.of("Price", 15.0, "Duration", 5.0, "OrderInStore", 1));
Var<Dictionary<string, Dictionary<string, object>>> speed = Var<Dictionary<string, Dictionary<string, object>>>.Define(
    "Powerups", new Dictionary<string, Dictionary<string, object>>() {
        { "Speed", new Dictionary<string, object>() {
            { "Price", 10 },
            { "Duration", 5 },
            { "Order in Store", 0 } } },
        { "Power", new Dictionary<string, object>() {
            { "Price", 15 },
            { "Duration", 5 },
            { "Order in Store", 1 } } }
        });

All of the above declarations will show up identically on our dashboard. Leanplum understands to group variables that use underscores (ObjC) or dots (Swift) in their names. The last method is convenient because instead of specifying the JSON data in code, you could potentially load it from a file and then convert it to a dictionary.

When the variable values are ready, you can get different slices of the variables using objectForKey[Path].

Using objectForKey

NSDictionary* allPowerups = [powerups objectForKeyPath:nil];
NSDictionary* speedPowerup = [powerups objectForKey:@"Speed"];
float speedPrice = [[powerups objectForKeyPath:@"Speed", @"Price", nil] floatValue];