SDK Introduction
Leanplum allows mobile teams to go from insight to action quickly using the lean cycle of releasing, analyzing, and optimizing content and messaging. See below to learn more about our SDK setup and features.
Setting up the SDK
Follow our setup guides to integrate the Leanplum SDK with your app.
Messaging
With Leanplum's messaging features, you can send customized messages to users and analyze your results from the Leanplum dashboard. You can also send messages manually via our API, depending on your organization's needs.
Once you integrate the Leanplum SDK, in-app messaging should be ready to use. Next, set up Push notification registration to send push notifications directly from the Leanplum dashboard. We also offer email services as an add-on feature, so your team can manage all of your app's messaging channels from one place.
Variables
Variables and resources will appear in the Variables tab automatically once they are detected by the SDK. After your variables are defined, you'll be able to change your variable values from the dashboard, without writing additional code or waiting for App Store approval.
You can preview variable changes on your device instantly before you send them out to your users. After you publish your changes, your users will see any updates the next time they open the app. You can also target variable changes to specific user segments to personalize their in-app experience.
After you install the SDK, see Defining variables and Variable types for more.
Analytics
With 19 out-of-the-box metrics, Leanplum's Analytics tab allows you to start gathering intelligence on your app from the moment the SDK is integrated. Our goal is to give you the data and tools you need to create an informed and results-driven content strategy. By seeing which metrics need improvement, you can decide whether to continue with your current strategy or A/B test a new one.
In addition to the metrics calculated automatically by Leanplum, you can also track custom Events and States in your app. Doing so allows you to create custom metrics in the Analytics dashboard. You can also trigger messages and other content changes for your users based on an event or state change.
A/B Testing
Testing different feature releases and messaging strategies ensures that you are rolling out the most effective content for your users. After you set up the Leanplum SDK (along with any custom variables, events, etc.) you'll be able to test different versions of messages or UI changes on a smaller pool of users before releasing changes to your entire userbase.
Once you've collected enough data, Leanplum will notify you of any statistically significant changes in your metrics. Then you can decide which message version or UI change you want to roll out to all of your users.
iOS setup
Get started setting up Leanplum with your app
Initial setup
The steps below use CocoaPods to integrate the Leanplum SDK.
Supports iOS 8.0 and above.
For more detail, you can also check out our generated iOS docs or review the source code on Github.
Download CocoaPods
CocoaPods is the dependency manager for Objective-C and Swift projects. If you haven't already, install CocoaPods by running the following command in the terminal:
$ sudo gem install cocoapods
For issues installing CocoaPods, see their website for help.
Add a Podfile
In the terminal, navigate to your app's directory. Add a Podfile to your app by running the following command:
$ pod init
Edit your Podfile
Open your Podfile by running the following command:
$ open -a Xcode Podfile
Insert the following into your Podfile:
use_frameworks!
pod 'Leanplum-iOS-SDK', ' '
# Uncomment one of the following for Location Services.
# pod 'Leanplum-iOS-Location', ' '
# pod 'Leanplum-iOS-LocationAndBeacons', ' '
See Geofencing and location for more on setting up location services.
Install your pods
Now, install the Leanplum SDK pods by running the following command:
$ pod install
If you prefer to use Carthage, you can simply add the following to your Cartfile.
github "Leanplum/Leanplum-iOS-SDK"
# Uncomment one of the following for Location Services.
# github "Leanplum/Leanplum-iOS-Location"
# github "Leanplum/Leanplum-iOS-LocationAndBeacons"
Add DEBUG flag (Swift only)
In your Build Settings, add -D DEBUG to Debug mode in Other Swift Flags.
Edit your AppDelegate
Remember to import Leanplum before doing anything. You can avoid this by adding the import to your app's prefix header (.pch) file.
For tighter security, remove your development key from your app delegate before submitting to the App Store.
import UIKit
#if DEBUG
import AdSupport
#endif
import Leanplum
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(application: UIApplication!, didFinishLaunchingWithOptions launchOptions: NSDictionary!) -> Bool {
// Get your App ID and Keys from https://www.leanplum.com/dashboard?#/account/apps
#if DEBUG
Leanplum.setDeviceId(ASIdentifierManager.shared().advertisingIdentifier.uuidString)
Leanplum.setAppId("YOUR_APP_ID",
developmentKey:"YOUR_DEVELOPMENT_KEY")
#else
Leanplum.setAppId("YOUR_APP_ID",
productionKey: "YOUR_PRODUCTION_KEY")
#endif
// Optional: Tracks in-app purchases automatically as the "Purchase" event.
// To require valid receipts upon purchase or change your reported
// currency code from USD, update your app settings.
// Leanplum.trackInAppPurchases()
// Optional: Tracks all screens in your app as states in Leanplum.
// Leanplum.trackAllAppScreens()
// Sets the app version, which otherwise defaults to
// the build number (CFBundleVersion).
Leanplum.setAppVersion("YOUR_APP_VERSION")
// Starts a new session and updates the app content from Leanplum.
Leanplum.start()
return true
}
...
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Get your App ID and Keys from https://www.leanplum.com/dashboard?#/account/apps
LEANPLUM_USE_ADVERTISING_ID;
[Leanplum setAppId:@"YOUR_APP_ID"
withDevelopmentKey:@"YOUR_DEVELOPMENT_KEY"];
[Leanplum setAppId:@"YOUR_APP_ID"
withProductionKey:@"YOUR_PRODUCTION_KEY"];
// Optional: Tracks in-app purchases automatically as the "Purchase" event.
// To require valid receipts upon purchase or change your reported
// currency code from USD, update your app settings.
// [Leanplum trackInAppPurchases];
// Optional: Tracks all screens in your app as states in Leanplum.
// [Leanplum trackAllAppScreens];
// Optional: Activates UI Editor.
// Requires the Leanplum-iOS-UIEditor framework.
// [[LeanplumUIEditor sharedEditor] allowInterfaceEditing];
// Sets the app version, which otherwise defaults to
// the build number (CFBundleVersion).
[Leanplum setAppVersion:@"2.4.1"];
// Starts a new session and updates the app content from Leanplum.
[Leanplum start];
return YES;
}
...
@end
App Version
The App Version in iOS defaults to the main bundle CFBundleVersion.
Verify your setup (iOS)
When testing your build, run your app in Debug/development mode using the Development key. Use the Production key when the app is pushed live (to be used by real users/in production).
The Development key is used to:
- Send data to the development/test pipeline (via an open web socket in real-time)
- Log processes in the debugger for validation
- See and register your developer devices in the dashboard
- Force your test device into specific A/B test variants
- Keep your data segregated from the live users (instead it will show up in the Developer Activity section)
- Update custom templates and variables to the content management system
Never use a development key in a production/live build.
A development key uses an open socket for real-time analytics — this pipeline cannot support real users (in a production build). Additionally, any user data will be lost as it is not captured in analytics.
Run your project and register your device
Registering a test device will allow you to test your messages, variable changes, and other Leanplum projects on a real device.
To register your device, first make sure you're in Debug mode. To ensure Debug mode is enabled, make sure the DEBUG preprocessor macro is set in Build Settings.
Next, go to Devices in the Leanplum dashboard, then hover over your device and click Register.
Test in-app messaging
Send yourself an in-app message to see it in action. While running your device in Debug mode, go to the Leanplum Message Composer. Create a new message and click Send Preview. The message will be sent to all registered test devices.
Create a custom variable (optional)
Variables you define will show up in the Leanplum dashboard, where you can change them, segment them, and A/B test them without having to update your app. You can verify the variable has been set by checking the Variables tab in the Leanplum dashboard.
Add the following lines to your app delegate or another class of your choosing:
import UIKit
import Foundation
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
var welcomeMessage = Var(name: "welcomeMessage", string: "Welcome to Leanplum!")
func application(application: UIApplication!, didFinishLaunchingWithOptions launchOptions: NSDictionary!) -> Bool {
...
Leanplum.onVariablesChanged({
NSLog(self.welcomeMessage.stringValue)
})
...
return true
}
...
}
DEFINE_VAR_STRING(welcomeMessage, @"Welcome to Leanplum!");
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{ ...
[Leanplum onVariablesChanged:^{
NSLog(@"%@", welcomeMessage.stringValue);
}];
return YES;
}
...
@end
See Defining variables for more on setting up variables.
It's important to use callbacks if the value is needed around the time the app starts. This guarantees the latest value. See more on how to handle asynchronous code with Callbacks.
Track an event (optional)
Events allow you to measure statistics about what your users do inside your app. You can verify an event is tracked in the Debugger or Events tabs in the Dashboard.
Add the following lines of code to track an event. You can place it anywhere after calling start; this is just an example:
import UIKit
import Foundation
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(application: UIApplication!, didFinishLaunchingWithOptions launchOptions: NSDictionary!) -> Bool {
...
Leanplum.onVariablesChanged({
Leanplum.track(event: "Launch")
})
...
return true
}
...
}
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{ ...
[Leanplum onVariablesChanged:^{
NSLog(@"%@", welcomeMessage.stringValue);
[Leanplum track:@"Launch"];
}];
return YES;
}
...
@end
See Events for more on tracking custom events with Leanplum.
iOS extensions
Leanplum can be used from iOS extensions. Before calling [Leanplum start] when your ViewController loads, you need to call [Leanplum setExtensionContext:self.extensionContext].
Android setup
The following setup guide is for developers using Gradle to manage dependencies. Supports Android 4.0.1 (API Level 14) and above.
For more detail, feel free to check out our generated Javadoc or view the source code on Github.
Use SDK version 4.2.X until you migrate to Android X.
SDK versions 4.3.X and above require Android X configuration. If you haven't migrated to Android X, use the most recent SDK version in the 4.2.X series. See here for Android's Android X migration guide.
Update your Gradle files
Leanplum Android SDK 5.4.2 requires Java 1.8 compiler. Ensure your gradle file uses that version:
android {
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
In your project-level build.gradle build file:
- add the Leanplum repo to your
allprojectrepositories. (this is not needed for Android SDK 4.3.0+) - add
google-servicesto your dependencies for Firebase Cloud Messaging.
Here's a simple example:
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.1.4'
// For Firebase Cloud Messaging.
classpath 'com.google.gms:google-services:4.2.0'
}
}
allprojects {
repositories {
jcenter()
maven {
url "https://repo.leanplum.com/" //only needed for Android SDK versions below 4.3.0
}
maven {
url "https://maven.google.com"
}
}
In your module-level build.gradle build file:
- Add the required Leanplum libraries in your dependencies, based on which Leanplum services you intend to use (see below).
In SDK 4.0+, our features are split into modular libraries.
leanplum-fcm
FCM enables push notifications through Leanplum.
leanplum-location
Enables location-based messaging. See more here.
leanplum-core
Enables core Leanplum functionality: in-app messages, a/b tests, states/events tracking, etc.
You can use any combination of these libraries except:
- You do not need to add
corewith other libraries (if using Gradle). All of our feature-specific libraries automatically includecore, so you should only addcoreif you don’t include any other Leanplum libraries.
Given that, here is a fully-loaded Gradle file.
dependencies {
// Firebase messaging.
// Minimum supported version of Firebase is 10.0.1.
implementation 'com.leanplum:leanplum-fcm: '
implementation 'com.google.firebase:firebase-messaging:17.5.0'
// Location services.
// Only include if you need location-based messaging.
// Minimum supported version of play location is:
// 10.0.1 for FCM.
implementation 'com.leanplum:leanplum-location: '
implementation 'com.google.android.gms:play-services-location:16.0.0'
}
apply plugin: 'com.google.gms.google-services'
Or, if you just want core functionality (aka "none of the above"):
dependencies {
// NONE OF THE ABOVE
// Automatically included with any/all of the above Leanplum libraries.
// Only include if you do not use any other Leanplum libraries.
implementation 'com.leanplum:leanplum-core: '
}
You can see more detailed examples in our guide to updating to Android SDK 4.0.
With previous versions of our SDK (< 4.0), you only needed to include com.leanplum:Leanplum. The Leanplum library included FCM and geolocation libraries.
Edit your manifest
On Android SDK 2.1.0+, we support manifest merging with Gradle only which will add all needed services into your manifest automatically, including FCM services.
Want to install our Android SDK manually?
Contact to your CSM or support team member for assistance.
Here's an example AndroidManifest.xml file:
<manifest package="[com.YOUR_PACKAGE]"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" >
<uses-sdk tools:overrideLibrary="com.leanplum, com.google.android.gms"/>
<!-- These permissions are required only for push notifications. -->
<!-- Optional. Prevents the device from sleeping when a message is received. -->
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<!-- These permissions are required only for geofencing. -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme"
android:name=".[YOUR_APPLICATION_CLASS]" >
<meta-data
android:name="com.google.android.gms.version"
android:value="@integer/google_play_services_version" />
...
</application>
</manifest>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="[com.YOUR_PACKAGE]"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="14" />
<!-- Base permissions for Leanplum Android SDK. -->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.INTERNET"/>
<!-- Optional. Prevents the device from sleeping when a message is received. -->
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE"/>
<permission android:name="[com.YOUR_PACKAGE].permission.C2D_MESSAGE"
android:protectionLevel="signature" />
<uses-permission android:name="[com.YOUR_PACKAGE].permission.C2D_MESSAGE" />
<!-- These permissions are required only for geofencing. -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme"
android:name=".[YOUR_APPLICATION_CLASS]" >
<meta-data
android:name="com.google.android.gms.version"
android:value="@integer/google_play_services_version" />
<receiver
android:name="com.leanplum.LeanplumPushReceiver"
android:exported="false" >
<intent-filter>
<action android:name="com.leanplum.LeanplumPushFirebaseMessagingService" />
</intent-filter>
</receiver>
<service android:name="com.leanplum.LeanplumLocalPushListenerService" />
<service android:name="com.leanplum.LeanplumPushRegistrationService" />
<service
android:name="com.leanplum.LeanplumPushFirebaseMessagingService"
android:exported="false">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
<!-- For geofencing only -->
<service android:name="com.leanplum.ReceiveTransitionsIntentService" />
<activity
android:name="[com.YOUR_PACKAGE].MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
Edit your application class
Add the following to your application class. You may need to create a new class that extends Application for this step.
If your application class does not extend LeanplumApplication, you must call enableLifecycleCallbacks (like the example below). Failure to do so will prevent the SDK from properly tracking states and displaying in-app messages on app resume.
You must set your AppIds in the Application class or in a class that extends Application.
For tighter security, remove the development key from your application class before submitting an app to the Play Store.
To use one or more push services alongside Leanplum's push service, you may need to create a custom class that extends the correct Leanplum push messaging class. See our guide here.
package [com.YOUR_PACKAGE];
import com.leanplum.Leanplum;
// For tracking user sessions.
import com.leanplum.LeanplumActivityHelper;
import com.leanplum.annotations.Parser;
import com.leanplum.annotations.Variable;
import com.leanplum.callbacks.StartCallback;
import com.leanplum.callbacks.VariablesChangedCallback;
public class ApplicationClass extends Application {
public void onCreate() {
super.onCreate();
Leanplum.setApplicationContext(this);
Parser.parseVariables(this);
// For session lifecyle tracking.
LeanplumActivityHelper.enableLifecycleCallbacks(this);
// Insert your API keys here.
if (BuildConfig.DEBUG) {
Leanplum.setAppIdForDevelopmentMode("Your App ID", "Your Development Key");
} else {
Leanplum.setAppIdForProductionMode("Your App ID", "Your Production Key");
}
// Optional: Tracks all screens in your app as states in Leanplum.
// Leanplum.trackAllAppScreens();
// This will only run once per session, even if the activity is restarted.
Leanplum.start(this);
...
}
...
}
Set up push notifications
Below are Firebase Cloud Messaging (FCM) setup instructions. To use this push service with Leanplum, see our guide to using multiple push services.
If you target Android 8.0 (API level 26) or above, you must register at least one notification channel in order to send notifications to users with devices running Android 8.0 (level 26) or later. See our guide to Using Android Notification Channels.
To use FCM with Leanplum, you need to:
- Copy your FCM Server Key. In the Firebase console, click the gear icon next to Overview, then click Project Settings.
Then, in your project's settings, go to the Cloud Messaging tab. In this section of your settings, you will see your Server key. Copy the key.
- Add your FCM Server Key to Leanplum. In the Leanplum dashboard, in your App Settings click Keys & Settings. Go to the Push Notifications tab and enter/paste your key into the Google API key field.
Add a push notification channel
Once you finish your push setup, you'll need to add a default Android Notification channels for your app using our API. This is required before you can start sending push notifications through Leanplum.
Add Proguard configuration
This is only needed in your proguard-project.txt if you use Proguard to obfuscate your code, and if you install via JAR. Our AAR library already includes this configuration.
-keepclassmembers class *
{
@com.leanplum.annotations.* <fields>;
}
-keep class com.leanplum.** { *; }
-dontwarn com.leanplum.**
Verify your setup (Android)
For testing purposes, run your app in Debug/development mode using the Development key. The Production key should be used when the app is pushed live and used by real users in production.
The Development key is used to:
- Send data to the development/test pipeline (via an open web socket in real-time)
- Log processes in the debugger for validation
- See and register your developer devices in the dashboard
- Force your test device into specific A/B test variants
- Keep your data segregated from the live users (instead it will show up in the Developer Activity section)
- Update custom templates and variables to the content management system
Never use a development key in a production/live build.
Using a development key utilizes an open socket for real-time analytics, but this pipeline cannot support real users (in a production build). Additionally, any user data will be lost as it is not captured in analytics.
Run your project and register your device
Registering a test device will allow you to test your messages, variable changes, and other Leanplum projects on a real device.
To register your device, first make sure you're in Debug mode. Then, from the Leanplum dashboard, go to Devices then hover over your device and click Register.
Next, go to Devices in the Leanplum dashboard, then hover over your device and click Register.
Test in-app messaging
Send yourself an in-app message to see it in action. While running your device in Debug mode, go to the Leanplum Message Composer. Create a new message and click Send Preview. The message will be sent to all registered test devices.
Create a custom variable (optional)
Variables you define will show up in the Leanplum dashboard, where you can change them, segment them, and A/B test them without having to update your app. You can verify the variable has been set by checking the Variables tab in the Leanplum dashboard.
Add the following lines to your application class or another class of your choosing:
import com.leanplum.annotations.Variable;
import com.leanplum.annotations.Parser;
public class ApplicationClass extends Application {
// All variables must be defined before calling Leanplum.start.
// Use the Parser class (see docs) since your variables are outside of your main activity.
public static String welcomeMessage = "Welcome to Leanplum!";
public void onCreate() {
...
// Be sure to set the context to this in the Parser.
Parser.parseVariables(this);
// It's important to use the variables changed callback if the value is needed
// around the time the app starts, so that we're guaranteed to have the latest value.
Leanplum.addVariablesChangedHandler(new VariablesChangedCallback() {
public void variablesChanged() {
Log.i("Test", welcomeMessage);
}
});
...
Leanplum.start();
}
}
See Defining variables for more on setting up variables.
Track an event (Optional)
Events allow you to measure statistics about what your users do inside your app. You can also use events to target users for certain messages, tests, or other content changes. You can verify when an event is tracked in the Debugger or the Events tab in the Leanplum dashboard.
Add the following lines of code to track an event. You can place the Leanplum track call anywhere after calling start. For example:
import com.leanplum.annotations.Variable;
import com.leanplum.annotations.Parser;
public class ApplicationClass extends Application {
@Variable public static String welcomeMessage = "Welcome to Leanplum!";
@Override
public void onCreate() {
...
Parser.parseVariables(this);
Leanplum.addVariablesChangedHandler(new VariablesChangedCallback() {
@Override
public void variablesChanged() {
Log.i("Test", welcomeMessage);
Leanplum.track("Launch");
}
});
...
Leanplum.start();
}
}
See Events for more on tracking events in Leanplum.
Unity setup
Our Unity SDK includes both a native Unity implementation of the Leanplum SDK as well as wrappers around the native iOS and Android SDKs. This setup allows us to provide feature parity on the iOS and Android platforms while still retaining support for other Unity platforms. The following matrix shows the features supported on each platform:
Unity SDK versions for Android X
If you are not on Android X yet, use the latest SDK version in the 1.6.X series. If you have Android X setup or your target is not Android, use the highest available SDK version.
Data modeling
Analytics
Unity asset bundles
-
-
In-app messaging
-
Push notifications
-
Install the SDK
Download the latest .unitypackage in the Leanplum Unity SDK.
Supports Unity 5.0 and above for Android, iOS, and Standalone platforms.
Leanplum's Unity SDK 1.7.0+ requires Unity version 2018.3 or above. This is because Unity SDK 1.7.0 and above requires Android X migration. See here for details.
Import the UnityPackage into your project
Make sure you import the directories Plugins and Standard Assets. Optionally import Editor, and/or LeanplumSample to include a sample project demonstrating how to manipulate the speed of rain with a Leanplum variable.
Add the Leanplum Prefab
Copy the Leanplum Prefab from Standard Assets/Leanplum into the first scene in your project that loads. Make sure the prefab is linked with the script LeanplumWrapper.cs. This GameObject will persist across scene changes so you don't have to worry about adding it to other scenes.
Add your app keys
Copy your app ID and keys into the Leanplum GameObject's inspector. You can find these in App Settings > Keys & Settings.
Initialize the SDK
The code in LeanplumWrapper.cs initializes the SDK. If you require asset bundle support on iOS and Android, you can switch to use the native Unity implementation for all platforms. However, this means that you won't get in-app messaging or push notification support.
The code snippets below exist only in the LeanplumSDK namespace. Remember to add the following to every source file that calls Leanplum's functions.
using LeanplumSDK;
See below for more on defining variables and tracking events.
Verify the setup
For testing purposes, run your app in Debug/development mode using the Development key. The Production key should be used when the app is pushed live and used by real users in production.
The Development key is used to:
- Send data to the development/test pipeline (via an open web socket in real-time)
- Log processes in the debugger for validation
- See and register your developer devices in the dashboard
- Force your test device into specific A/B test variants
- Keep your data segregated from the live users (instead it will show up in the Developer Activity section)
- Update custom templates and variables to the content management system
Never use a development key in a production/live build.
A development key uses an open socket for real-time analytics, but this pipeline cannot support real users (in a production build). Additionally, any user data will be lost as it is not captured in analytics.
Run your project and register your device
Make sure the Development Build checkbox is checked in Unity's Build Settings menu. From the Leanplum dashboard, go to Devices then hover over your device and click Register.
Create a custom variable (optional — Unity)
The variables you define with Leanplum will show up in the Leanplum dashboard, where you can change them, segment them, and A/B test them without having to update your app. You can verify the variable has been set by viewing the Variables tab in Leanplum.
Add the highlighted lines to your app delegate or another class of your choosing:
using LeanplumSDK;
public class LeanplumWrapper : MonoBehaviour
{ ...
private Var<string> welcomeMessage;
void Start()
{ ...
// Avoid assigning the variable where its declared up top becuase
// that code may run multiple times and will produce an error.
welcomeMessage = Var<string>.Define("welcomeMessage", "Welcome to Leanplum!");
Leanplum.Start();
}
void OnEnable()
{
// It's important to use the variables changed callback or welcomeMessage.ValueChanged if the
// value is needed around the time the app starts, so that we're guaranteed to have the latest value.
Leanplum.VariablesChanged += OnLeanplumOnVariablesChanged;
}
void OnDisable()
{
Leanplum.VariablesChanged -= OnLeanplumOnVariablesChanged;
}
private void OnLeanplumOnVariablesChanged()
{
Debug.Log(welcomeMessage.Value);
}
}
See Defining variables for more on using variables with Leanplum.
Track an event (optional)
Events allow you to measure statistics about what your users do inside your app. You can also trigger messages and other content changes after certain events. You can verify an event is tracked in the Debugger or Events tabs in the Dashboard.
Add the highlighted line of code to track an event. You can place it anywhere after calling start. Example:
using LeanplumSDK;
public class LeanplumWrapper : MonoBehaviour
{ ...
void Start()
{ ...
Leanplum.Track("Launch");
}
}
See Events for more on tracking events with Leanplum.
JavaScript setup
Leanplum JavaScript SDK allows you to:
- Collect data from your Web App, Progressive Web App or other JavaScript-based platforms
- Send Push Notifications to Web Browsers
- Send Inbox Messages to Web Apps
- Utilize Variables in Web Apps
Install the SDK
Using NPM
The Leanplum JavaScript SDK is also available as an NPM package. Run npm install leanplum-sdk to install add it in your project.
Using a CDN
You can use the jsdelivr CDN to load the JS SDK:
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/leanplum.min.js"></script>
Download
Alternatively, you can download the Leanplum JavaScript SDK (version 1.8.0)
<script type="text/javascript" src="leanplum.js"></script>
Using Web Push messaging
To use web push in your app, you must add the Service Worker file sw.min.js from our SDK to your root directory, then register the Service Worker and user for push notifications.
For more see Push notifications.
Initialize Leanplum
// This value should be set to true only if you're developing on your server.
var isDevelopmentMode = true;
// Sample variables. This can be any JSON object.
var variables = {
items: {
color: 'red',
size: 20,
showBadges: true
},
showAds: true
};
// Get your App ID and Keys from https://www.leanplum.com/dashboard?#/account/apps
if (isDevelopmentMode) {
Leanplum.setAppIdForDevelopmentMode("YOUR_APP_ID", "YOUR_DEVELOPMENT_KEY");
} else {
Leanplum.setAppIdForProductionMode("YOUR_APP_ID", "YOUR_PRODUCTION_KEY");
}
Leanplum.setVariables(variables);
Leanplum.start(function(success) {
console.log('Success: ' + success);
console.log('Variables', Leanplum.getVariables());
});
Verify the setup
For testing purposes, run your app in Debug/development mode using the Development key. The Production key should be used when the app is pushed live and used by real users in production.
The Development key is used to:
- Send data to the development/test pipeline (via an open web socket in real-time)
- Log processes in the debugger for validation
- See and register your developer devices in the dashboard
- Force your test device into specific A/B test variants
- Keep your data segregated from the live users (instead it will show up in the Developer Activity section)
- Update custom templates and variables to the content management system
Never use a development key in a production/live build.
Using a development key utilizes an open socket for real-time analytics, but this pipeline cannot support real users (in a production build). Additionally, any user data will be lost as it is not captured in analytics.
Register your device
Be sure to run your app in development mode. From the Leanplum dashboard, go to Devices then hover over your device and click Register.
How to track events in your app
Leanplum.track("View Cart");
Leanplum.track("View Cart", {itemsInCart: 4});
Leanplum.track("Purchase", 4.99, {itemCategory: 'Apparel', itemName: 'Shoes'});
How to track states transitions. These are sections of your app the user is in.
Leanplum.advanceTo("Cart");
Leanplum.advanceTo("Level", level.name);
// The 'null' state. Causes the user to leave the current state and not enter another one.
Leanplum.advanceTo(null);
Passing custom user IDs.
Leanplum.start('mike3958');
Passing user attributes. User attributes are applied to the user, so they're saved across sessions. If an attribute no longer applies, you can set it to null.
Leanplum.start({hasFacebookProfile: true});
Leanplum.start('userId', {age: 20, gender: 'Male'}, callback);
Starts Leanplum by simply loading the cached variables, and doesn't log a new session. Useful for subsequent page loads inside the app.
Leanplum.startFromCache('mike3958');
How to track the session lifecycle.
Leanplum.pauseSession(); // Pauses the current session.
Leanplum.resumeSession(); // Resumes the current session.
Leanplum.stop(); // Ends the current session. Sessions will automatically timeout after 2 hours if stop isn't called explicitly (30 minutes if paused).
Javascript SDK settings
Set the path of the API server.
Leanplum.setApiPath('https://api.leanplum.com/api');
Sets the network timeout in seconds. (Default: 10).
Leanplum.setNetworkTimeout(5);
Sets the request batching strategy. This applies to production mode only. The default behavior is to batch every 5 seconds.
Leanplum.setRequestBatching(false); // No batching.
Leanplum.setRequestBatching(true); // Batching enabled. Unsent requests are sent on start, pauseSession, resumeSession, and stop
Leanplum.setRequestBatching(true, 30); // Same as above except unsent requests are also sent every 30 seconds.
Enables development mode, and sets the app ID and client key.
Leanplum.setAppIdForDevelopmentMode('appId', 'clientKey');
Enables production mode, and sets the app ID and client key.
Leanplum.setAppIdForProductionMode('appId', 'clientKey');
Sets a custom device ID. If this is not called, Leanplum will assign a device ID automatically.
Leanplum.setDeviceId('DevId');
Sets the current version of your app.
Leanplum.setAppVersion('1.0.1');
Sets the device name to override the default.
Leanplum.setDeviceName("Andrew's iMac");
Sets the device model to override the default.
Leanplum.setDeviceModel('Mac');
Sets the system name to override the default.
Leanplum.setSystemName('Mac OS X');
Sets the system version to override the default.
Leanplum.setSystemVersion('10.8');
React Native setup
React Native setup
Using NPM
The Leanplum React Native SDK is available as NPM package. Run npm install @leanplum/react-native-sdk to install add it in your project.
Linking to native dependency
If you are using ReactNative 0.60 or greater, you can take advantage of autolinking.
For Android, gradle will take care of all dependencies.
For iOS only command that needs to be run is:
cd ios && pod install
On older version, linking has to be done manually by executing:
npx react-native link @leanplum/react-native-sdk
andcd ios && pod install
Additional Android setup
In your generated Android MainApplication.java file add additional code to properly setup Leanplum SDK
import com.leanplum.Leanplum;
import com.leanplum.LeanplumActivityHelper;
...
public class MainApplication extends Application implements ReactApplication {
...
public void onCreate() {
super.onCreate();
...
Leanplum.setApplicationContext(this);
Parser.parseVariables(this);
// For session lifecyle tracking.
LeanplumActivityHelper.enableLifecycleCallbacks(this);
}
...
}
// This value should be set to true only if you're developing on your server.
const isDevelopmentMode = true;
// Sample variables. This can be any JSON object.
const variables = {
items: {
color: "red",
size: 20,
showBadges: true
},
showAds: true
};
// Get your App ID and Keys from https://www.leanplum.com/dashboard?#/account/apps
if (isDevelopmentMode) {
Leanplum.setAppIdForDevelopmentMode("YOUR_APP_ID", "YOUR_DEVELOPMENT_KEY");
} else {
Leanplum.setAppIdForProductionMode("YOUR_APP_ID", "YOUR_PRODUCTION_KEY");
}
Leanplum.setVariables(variables);
Leanplum.start();
Register your device
Be sure to run your app in development mode. From the Leanplum dashboard, go to Devices then hover over your device and click Register.
How to track events in your app
Leanplum.track(EVENT)
Leanplum.track(EVENT, {[PARAMETER_KEY]: PARAMETER_VALUE})
// Example
Leanplum.track("Purchase", 4.99, {itemCategory: 'Apparel', itemName: 'Shoes'});
How to track states transitions. These are sections of your app the user is in.
Leanplum.advanceTo(STATE, INFO, { [PARAMETER_KEY]: PARAMETER_VALUE });
// Example
Leanplum.advanceTo("Cart");
// The 'null' state. Causes the user to leave the current state and not enter another one.
Leanplum.advanceTo(null);
Leanplum.pauseState();
Leanplum.resumeState();
Leanplum.setAppIdForDevelopmentMode(YOUR_APP_ID, YOUR_DEVELOPMENT_KEY);
Enables production mode, and sets the app ID and client key.
Leanplum.setAppIdForProductionMode(YOUR_APP_ID, YOUR_PRODUCTION_KEY);
Sets a custom device ID. If this is not called, Leanplum will assign a device ID automatically.
Leanplum.setDeviceId(DEVICE_ID)
Manual SDK setup
We generally recommend installing the SDK using our dependency managers, but if you'd rather set up your SDK manually, see below for iOS and Android instructions.
- iOS manual setup
- Android manual setup (See note for manual steps.)
Manual setup iOS
Setting up the SDK manually
You can install our SDK manually using the steps below for iOS.
Install the SDK
First, Download Leanplum's iOS SDK.
Supports iOS 7.0 and above.
Add Leanplum.framework to your project
Drop the .framework package into the "Embedded Binaries" list in your targets > general tab. This will add the .framework to "Linked Frameworks and Libraries" as well.
If you are using the static version of our framework, you only need to add it to "Linked Frameworks and Libraries".
If you want to use Location services on Leanplum, add LeanplumLocation.framework. Alternatively, if you also need iBeacon services on Leanplum, add LeanplumLocationAndBeacons.framework. You can download these from GitHub.
Add Leanplum frameworks to your project
Add CFNetwork, SystemConfiguration, Security, AdSupport, and StoreKit frameworks. AdSupport and StoreKit can be marked as Optional. IDFA is only used (optionally) in development mode and not in the build sent to the App Store. If you're linking LeanplumLocation or LeanplumLocationAndBeacons, add CoreLocation.
Add the -ObjC -fobjc-arc linker flags (optional)
This is only needed if your project has CLANG_ENABLE_OBJC_ARC or Objective-C Automatic Reference Counting set to NO (aka ARC disabled). By default ARC is enabled, so this flag is only needed if you have disabled ARC.
The -ObjC flag may not be compatible with certain libraries. You can replace it with -force_load ${SRCROOT}/Leanplum.framework/Leanplum. Make sure the path matches the location of Leanplum.framework on your filesystem.
Strip framework script (for release builds)
In order to submit a release build of the app to the App Store, you must make sure that the following script is in place to strip the framework to valid architectures. Navigate to 'Build Phases' in your project, add 'New Run Script', and move the 'New Run Script' after the Embed Frameworks. Then add the following code:
cd "${BUILT_PRODUCTS_DIR}/${FRAMEWORKS_FOLDER_PATH}/Leanplum.framework/"
lipo -remove i386 -remove x86_64 Leanplum -output Leanplum
Make sure to check 'Run script only when installing'. Once completed, the setup should look like the following:
Edit your AppDelegate
Remember to import Leanplum before doing anything. You can avoid this by adding the import to your app's prefix header (.pch) file.
For tighter security, remove your development key from your app delegate before submitting to the App Store.
import UIKit
#if DEBUG
import AdSupport
#endif
import Leanplum
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(application: UIApplication!, didFinishLaunchingWithOptions launchOptions: NSDictionary!) -> Bool {
// Get your App ID and Keys from https://www.leanplum.com/dashboard?#/account/apps
#if DEBUG
Leanplum.setDeviceId(ASIdentifierManager.shared().advertisingIdentifier.uuidString)
Leanplum.setAppId("YOUR_APP_ID",
withDevelopmentKey:"YOUR_DEVELOPMENT_KEY")
#else
Leanplum.setAppId("YOUR_APP_ID",
withProductionKey: "YOUR_PRODUCTION_KEY")
#endif
// Optional: Tracks in-app purchases automatically as the "Purchase" event.
// To require valid receipts upon purchase or change your reported
// currency code from USD, update your app settings.
// Leanplum.trackInAppPurchases()
// Optional: Tracks all screens in your app as states in Leanplum.
// Leanplum.trackAllAppScreens()
// Optional: Activates UI Editor.
// Requires the Leanplum-iOS-UIEditor framework.
// LeanplumUIEditor.shared().allowInterfaceEditing()
// Starts a new session and updates the app content from Leanplum.
Leanplum.start()
return true
}
...
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Get your App ID and Keys from https://www.leanplum.com/dashboard?#/account/apps
LEANPLUM_USE_ADVERTISING_ID;
[Leanplum setAppId:@"YOUR_APP_ID"
withDevelopmentKey:@"YOUR_DEVELOPMENT_KEY"];
[Leanplum setAppId:@"YOUR_APP_ID"
withProductionKey:@"YOUR_PRODUCTION_KEY"];
// Optional: Tracks in-app purchases automatically as the "Purchase" event.
// To require valid receipts upon purchase or change your reported
// currency code from USD, update your app settings.
// [Leanplum trackInAppPurchases];
// Optional: Tracks all screens in your app as states in Leanplum.
// [Leanplum trackAllAppScreens];
// Optional: Activates UI Editor.
// Requires the Leanplum-iOS-UIEditor framework.
// [[LeanplumUIEditor sharedEditor] allowInterfaceEditing];
// Sets the app version, which otherwise defaults to
// the build number (CFBundleVersion).
[Leanplum setAppVersion:@"2.4.1"];
// Starts a new session and updates the app content from Leanplum.
[Leanplum start];
return YES;
}
...
@end
Verify the setup
Verify the setup
When testing your build, run your app in Debug/development mode using the Development key. Use the Production key when the app is pushed live (to be used by real users/in production).
The Development key is used to:
- Send data to the development/test pipeline (via an open web socket in real-time)
- Log processes in the debugger for validation
- See and register your developer devices in the dashboard
- Force your test device into specific A/B test variants
- Keep your data segregated from the live users (instead it will show up in the Developer Activity section)
- Update custom templates and variables to the content management system
Never use a development key in a production/live build.
Using a development key utilizes an open socket for real-time analytics, but this pipeline cannot support real users (in a production build). Additionally, any user data will be lost as it is not captured in analytics.
Run your project and register your device
Make sure you're in Debug mode. To ensure Debug mode is enabled, make sure the DEBUG preprocessor macro is set in Build Settings.
From the Leanplum dashboard, go to Devices then hover over your device and click Register.
Test in-app messaging
Send yourself an in-app message to see it in action. While running your device in Debug mode, go to the Leanplum Message Composer. Create a new message and click Send Preview. The message will be sent to all registered test devices.
Create a custom variable (optional)
The variables you define will show up in the Leanplum dashboard, where you can change them, segment them, and A/B test them without having to update your app. You can verify the variable has been set by viewing the Variables tab in the Leanplum dashboard.
Add the following lines to your app delegate or another class of your choosing:
import UIKit
import Foundation
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
var welcomeMessage = LPVar.define("welcomeMessage",
withString: "Welcome to Leanplum!")
func application(application: UIApplication!, didFinishLaunchingWithOptions launchOptions: NSDictionary!) -> Bool {
...
Leanplum.onVariablesChanged({
NSLog((self.welcomeMessage?.stringValue())!)
})
...
return true
}
...
DEFINE_VAR_STRING(welcomeMessage, @"Welcome to Leanplum!");
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{ ...
[Leanplum onVariablesChanged:^{
NSLog(@"%@", welcomeMessage.stringValue);
}];
return YES;
}
...
@end
It's important to use the variables changed callback onVariablesChanged, or welcomeMessage.onValueChanged (for our example variable welcomeMessage), if the value is needed around the time the app starts. This guarantees the latest value. See more on how to handle asynchronous code with Callbacks.
For more on Variables, see Defining variables.
Track an event (optional)
Events allow you to measure statistics about what your users do in your app. You can also use events to trigger messages and other content changes. You can verify an event is tracked in the Debugger or Events tabs in the Leanplum dashboard.
Add the following lines of code to track an event. You can place it anywhere after calling start. Example:
import UIKit
import Foundation
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
var welcomeMessage = LPVar.define("welcomeMessage", withString: "Welcome to Leanplum!")
func application(application: UIApplication!, didFinishLaunchingWithOptions launchOptions: NSDictionary!) -> Bool {
...
Leanplum.onVariablesChanged({
NSLog((self.welcomeMessage?.stringValue())!)
Leanplum.track("Launch")
})
...
return true
}
...
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{ ...
[Leanplum onVariablesChanged:^{
NSLog(@"%@", welcomeMessage.stringValue);
[Leanplum track:@"Launch"];
}];
return YES;
}
...
@end
For more on tracking events with Leanplum, see Events.
Manual setup Android
Integrating the Android SDK without without a dependency manager (non-Gradle setup)
For a manual setup, follow the steps in our Android SDK setup until you get to the Edit your manifest step.
Next, download our SDK. Be sure to drag the .jar file for each required library into your project's libs/ folder. Also include leanplum-core (and leanplum-push if using either push library).
Use SDK version 4.2.x until you migrate to Android X.
SDK versions 4.3.1+ require Android X configuration. If you haven't migrated to Android X, use the latest version in the 4.2.x series. See here for Android's Android X migration guide.
See the .jar manifest file below for more info:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="[com.YOUR_PACKAGE]"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="14" />
<!-- Base permissions for Leanplum Android SDK. -->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.INTERNET"/>
<!-- Optional. Prevents the device from sleeping when a message is received. -->
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE"/>
<permission android:name="[com.YOUR_PACKAGE].permission.C2D_MESSAGE"
android:protectionLevel="signature" />
<uses-permission android:name="[com.YOUR_PACKAGE].permission.C2D_MESSAGE" />
<!-- These permissions are required only for geofencing. -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme"
android:name=".[YOUR_APPLICATION_CLASS]" >
<meta-data
android:name="com.google.android.gms.version"
android:value="@integer/google_play_services_version" />
<receiver
android:name="com.leanplum.LeanplumPushReceiver"
android:exported="false" >
<intent-filter>
<action android:name="com.leanplum.LeanplumPushFirebaseMessagingService" />
</intent-filter>
</receiver>
<service android:name="com.leanplum.LeanplumLocalPushListenerService" />
<service android:name="com.leanplum.LeanplumPushRegistrationService" />
<service
android:name="com.leanplum.LeanplumPushFirebaseMessagingService"
android:exported="false">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
<!-- For geofencing only -->
<service android:name="com.leanplum.ReceiveTransitionsIntentService" />
<activity
android:name="[com.YOUR_PACKAGE].MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
Variables
Defining variables with the Leanplum SDK
You can create variables that can take on new values from the server. Using variables in the Leanplum dashboard 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.
When you define a variable in your code, it will appear in the Variables tab in Leanplum the next time your app starts in development mode.
iOS
It's important to define all of your variables before calling Leanplum.Start. Use periods to group variables on the dashboard.
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 for examples.
Swift
Swift doesn't support macros like Objective-C. Instead, define variables using the Var class directly. Use dots in the name to group values into a structured variable. See Modeling structured data for examples.
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.
See Variable types for more specifics on each type of variable.
Android (Java)
There are two ways to define variables.
- Using the
@Variableannotation on public static class members
This is the easiest and most convenient way to define variables.
public class MainActivity extends LeanplumActivity {
public static String welcomeLabel = "Welcome!";
...
}
You can use this method to define variables:
- Inside your Application class if it extends
LeanplumApplication. - In your main activity if it extends one of the
LeanplumActivityclasses and callsLeanplum.start. - In another class. Use the
Parserclass to detect the annotations before callingLeanplum.start.
If you choose to define variables in an Activity or Class that does make your start call, you will need to use Parser.parseVariablesForClasses to collect these variable definitions before your start call.
For example, if we have variables defined in ClassA and ClassB, we need to add the following to our ApplicationClass before start:
...
import com.leanplum.annotations.Parser;
...
public class ApplicationClass extends LeanplumApplication {
public void onCreate() {
super.onCreate();
Leanplum.setApplicationContext(this);
...
// Parse variables from other classes.
Parser.parseVariablesForClasses(ClassA.class, ClassB.class);
// Then, call start.
Leanplum.start();
}
}
If you define your variables in an ApplicationClass or Activity where you call start, and the class does not extend LeanplumApplication or LeanplumActivity, then you must use the Parser class to detect that class's variable annotations.
...
import com.leanplum.annotations.Parser;
...
public class ApplicationClass extends Application {
public static String welcome = "Hi there!";
public void onCreate() {
super.onCreate();
Leanplum.setApplicationContext(this);
...
// Parse variables from this class.
Parser.parseVariables(this);
// Then, call start.
Leanplum.start();
}
}
- Using Var.define
public class MainActivity extends LeanplumActivity {
public static Var<String> welcomeLabel = Var.define("welcomeLabel", "Welcome!");
...
}
You must define variables with this method before calling Leanplum.start.
Unity
It's important to define all of your variables before calling Leanplum.Start. Use periods to group variables on the dashboard.
JavaScript (HTML5)
Sets your variables:
Leanplum.setVariables({
StoreTitle: "Powerup Store",
Items: [
{
name: "Speed Boost",
price: 100
}, {
name: "Health Boost",
price: 150
}
]
});
Gets your variables:
var variables = Leanplum.getVariables();
//Gets a particular variable.
var title = Leanplum.getVariable('StoreTitle');
var speedBoost = Leanplum.getVariable('Items', 0);
var healthBoostName = Leanplum.getVariable('Items', 1, 'name');
To resolve a file variable filename to a path, call the getFileUrl method, available in SDK versions 1.7.0 and later.
var filename = Leanplum.getVariable('SplashImage');
var url = Leanplum.getFileUrl(filename);
Leanplum.setVariables({
stringVar: 'Some string variable',
numVar: 1,
boolVar: true,
mapVar: {
stringVal: 'some string val',
'numVal:': 5,
},
listVar: [1, 2, 3],
});
// Example
Leanplum.setVariables({
title: "Welcome",
isFeatureEnabled: true,
gameOptions: {
speed: 60,
price: 100
}
});
Set asset:
Leanplum.setVariableAsset(ASSET_VARIABLE_NAME, path, (newPath: string) => {
// handle image
});
Get variables will return all variables that were set by setVariables() before calling start method.
Leanplum.getVariables();
Get variable with name will return only variable that was set by setVariables() before calling start method.
Leanplum.getVariables("stringVar");
// Example
Leanplum.getVariables("isFeatureEnabled");
Get asset:
Leanplum.getVariableAsset(ASSET_VARIABLE_NAME);
Variable types
Examples of how to set and access variable values for different types of variables in Leanplum.
//Define the variable and set the name and value using the Var class. To access the value in your code, use the floatValue method.
var shootSpeed = Var(name: "shootSpeed", float: 1.0) // How fast your ship shoots.
...
Leanplum.onVariablesChanged {
// Move ship according to its speed.
myShip.moveWithSpeed(shootSpeed.floatValue())
}
//Set the value with the macro DEFINE_VAR_FLOAT. 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];
}];
public static float shootSpeed = 1; // How fast your ship shoots.
Var<float> shootSpeed = Var<float>.Define("shootSpeed", 1.0); // How fast your ship shoots.
...
void MoveShip() {
MoveWithSpeed(shootSpeed.Value);
}
//Define the variable and set the name and value using the Var class. To access the value in your code, use the boolValue method.
var showAds = Var(name: "showAds", boolean: false) // Whether or not to show ads in the app.
...
Leanplum.onVariablesChanged {
if showAds.boolValue() {
self.view.addSubview(adView)
}
}
//Set the value with the macro DEFINE_VAR_BOOL. 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];
}
}];
// The variable showAds will show up on our dashboard as "Show Ads".
(name="Show Ads") public static boolean showAds = false; // Whether or not to show ads in the app.
// Boolean with custom name: the variable showAds will show up on our dashboard as "Show Ads".
Var<bool> showAds = Var<bool>.Define("Show Ads", false);
...
void ShowAds() {
if (showAds.Value) {
MakeAdBanner();
}
}
//Define the variable and set the name and value using the Var class. To access the value in your code, use stringValue.
var startLabel = Var(name: "startLabel", string: "Start") // Label of the "Start" button
...
Leanplum.onVariablesChanged {
let startButton: UIButton = UIButton()
startButton.setTitle(startLabel.stringValue, for: .normal)
self.view.addSubview(startButton)
}
//Set the value with the macro DEFINE_VAR_STRING. 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];
}];
// The variable startLabel will show up on our dashboard within the group "mainScreen".
(group="mainScreen") public static String startLabel = "Start"; // Label of the "Start" button.
// Use "." to nest groups. You can also use "." with the "name" argument.
(group="screens.mainScreen") public static String startLabel = "Start"; // Label of the "Start" button.
String
Var<string> startLabel = Var<string>.Define("startLabel", "Start"); // Label for "Start" button.
...
void CreateStartButton() {
AddButtonWithText(startLabel.Value);
}
//Define the variable and set the name and value using the Var class. To access the value in your code, use the colorValue method.
var myColor = Var(name: "myColor", color: UIColor.gray)
...
Leanplum.onVariablesChanged {
startButton.setTitleColor(myColor.colorValue(), for: .normal)
}
//Set the value with the macro DEFINE_VAR_COLOR. 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];
}];
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.
//Define the variable and set the name and value using the Var class. Use the overload with file. To access the value in your code, use the fileValue or imageValue method.
// Image
var goldStar = Var(name: "goldStar", file: "gold_star.png") // Location of Gold Star image file.
...
Leanplum.onVariablesChanged {
self.splashView?.image = goldStar.imageValue()
}
//Set the value with the macro DEFINE_VAR_FILE. 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];
}];
public static Var<String> mario = Var.defineAsset("Mario", "Mario.png");
...
mario.addFileReadyHandler(new VariableCallback<String>() {
public void handle(Var<String> variable) {
im.setImageBitmap(BitmapFactory.decodeStream(mario.stream()));
}
});
// It is necessary to specify platform-specific AssetBundles as they are not cross-platform.
// The filenames specified when defining an AssetBundle represent the default
// bundles that will be loaded if present in Leanplum's file manager.
Var<AssetBundle> background = Var<AssetBundle>.DefineAssetBundle(
"forestBackground",
standaloneBundleName: "Standalone-Forest.unity3d",
androidBundleName: "Android-Forest.unity3d",
iosBundleName: "iOS-Forest.unity3d");
...
void LoadBackground() {
if (background.Value != null) {
Instantiate(background.Value.mainAsset);
}
}
//Define the variable and set the name and value using the Var class, with dictionary.
//To access a dictionary value in your code, use the objectForKey method to get to the correct property, then use an accessor method (boolValue, floatValue, etc.) to get the actual value.
//In Swift, you may need to cast after using objectForKey.
let powerUp = Var(name: "powerUp", dictionary: [
"name": "Turbo Boost",
"price": 150,
"speedMultiplier": 1.5,
"timeout": 15])
...
Leanplum.onVariablesChanged {
self.speed = (powerUp?.object(forKey: "speedMultiplier") as! NSNumber).floatValue
}
//Set the value with the macro DEFINE_VAR_DICTIONARY_WITH_OBJECTS_AND_KEYS. 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.
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];
}];
public static Map<String, Object> powerup = new HashMap<String, Object>() {
{
put("name", "Turbo Boost");
put("price", 150);
put("speedMultiplier", 1.5);
put("timeout", 15);
put("slots", Arrays.asList(1, 2, 3));
}
};
Dictionary<string, object> powerupInit = new Dictionary<string, object>();
powerupInit.Add("price", 150);
powerupInit.Add("speedMultiplier", 1.5);
powerupInit.Add("timeout", 15);
Var<Dictionary<string, object>> powerup = Var<Dictionary<string, object>>.Define("powerup", powerupInit);
//Define the variable and set the name and value using the Var class.
//To access an array value in your code, use the objectAtIndex method to get to the correct object, then use an accessor method (boolValue, floatValue, etc.) to get the actual value.
//In Swift, you may need to cast after using objectAtIndex.
let storeItemsOrder = Var(name: "storeItemsOrder", array: [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
}
}
//Set the value with the macro DEFINE_VAR_ARRAY_WITH_OBJECTS. 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.
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);
}
}];
public static List<Integer> storeItemsOrder = Arrays.asList(1, 2, 3, 4);
Var<List<Object>> storeItemsOrder = Var<List<Object>>.Define("storeItemsOrder", new List<Object> { 0, 1, 2, 3, 4 });
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 a '.' are grouped automatically.
var speedPrice = Var(name: "Powerups.Speed.Price", integer:10)
var speedDuration = Var(name: "Powerups.Speed.Duration", integer:5)
var speedOrder = Var(name: "Powerups.Speed.OrderInStore", integer:0)
var powerPrice = Var(name: "Powerups.Power.Price", integer:15)
var powerDuration = Var(name: "Powerups.Power.Duration", integer:5)
var powerOrder = Var(name: "Powerups.Power.OrderInStore", integer:1)
// 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.
(name="Powerups.Speed.Price") double speedPrice = 10;
(name="Powerups.Speed.Duration") double speedDuration = 5;
(name="Powerups.Speed.OrderInStore") int speedOrder = 0;
(name="Powerups.Power.Price") double powerPrice = 15;
(name="Powerups.Power.Duration") double powerDuration = 5;
(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:
var powerUpsSpeed = Var(name: "Powerups.Speed", dictionary: [
"Price": 10,
"Duration": 5,
"OrderInStore": 0
])
var powerUpsPower = Var(name: "Powerups.Power", dictionary: [
"Price": 15,
"Duration": 5,
"OrderInStore": 1
])
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);
// Using the Google Guava library for brevity.
(group="Powerups") Map<String, Object> speed =
ImmutableMap.of("Price", 10.0, "Duration", 5.0, "OrderInStore", 0);
(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:
var powerups = Var(name: "Powerups", dictionary: [
"Speed": [
"Price": 10,
"Duration": 5,
"OrderInStore": 0
],
"Power": [
"Price": 15,
"Duration": 5,
"OrderInStore": 1
]
])
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}
}];
}
}
;
public static Map<String, Map<String, Object>> powerups = new HashMap<String, Map<String, Object>>() {
{ put("Speed", new HashMap<String, Object>() {
{ put("Price", 10.0); put("Duration", 5.0); put("OrderInStore", "OrderInStore"); }
});
put("Power", new HashMap<String, Object>() {
{ put("Price", 15.0); put("Duration", 5.0); put("OrderInStore", "OrderInStore"); }
});
}};
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];
Map<String, Object> allPowerups = powerups.objectForKeyPath();
Map<String, Object> speedPowerup = powerups.objectForKeyPath("Speed");
float speedPrice = powerups.objectForKeyPath("Speed", "Price");
Callbacks
Because Leanplum variables and resources are retrieved from the server asynchronously after start, you need to wait for the values to be downloaded before using them in your code. The proper way to do this is to use one of the callbacks provided by our SDK.
The SDK will use locally-cached values, if available, and changes will only be synced on start or forceContentUpdate.
You can use callbacks multiple times across different classes in your app. However, where you place the callback may influence when it is executed. If you call one after the necessary variables (and/or files) for that callback have been synced, the code in the callback will just execute again immediately.
Timing
All callbacks are executed on start, regardless of whether the file(s) or variable(s) have changed. The distinction between our callbacks is their behavior on forceContentUpdate — namely, which files or variables will trigger the callback. You can watch a single variable, a single file, all variables, or all variables and all files.
You must be on version 1.3.0 or newer of our Javascript SDK for the forceContentUpdate method.
startfinishes.
startfinishes.forceContentUpdatefinishes and a specific variable has changed.
startfinishes.forceContentUpdatefinishes and any variable has changed.
startfinishes.forceContentUpdatefinishes and a specific file has changed.
startfinishes.forceContentUpdatefinishes and a file or variable has changed.
After Leanplum start
This callback is executed only when the start call finishes and all variables and files are returned from the Leanplum server. It will not be executed on forceContentUpdate.
You can use this callback with a splash screen to wait and then load a new view after start finishes, so you don't have to worry about checking when your variables have their values.
// Add a callback.
Leanplum.onStartResponse{ (success) in
// Insert code here.
}
Leanplum.start()
// Or, add a responder that will be executed as a callback.
Leanplum.addStartResponseResponder(self, with: #selector(mySelector(success:)))
func mySelector(success:Bool){
// Insert code here.
}
// Add a callback.
[Leanplum onStartResponse:^(BOOL success) {
// Insert code here.
}];
[Leanplum start];
// Or, add a responder that will be executed as a callback.
[Leanplum addStartResponseResponder:self withSelector:@selector(mySelector:)];
- (void)mySelector:(BOOL) success {
// Insert code here.
}
// Add a new callback.
Leanplum.addStartResponseHandler(new StartCallback() {
public void onResponse(boolean b) {
// Insert code here.
}
});
// Add a callback.
Leanplum.Started += delegate(bool success) {
// Insert code here.
};
Leanplum.Start();
// Add a callback.
Leanplum.addStartResponseHandler(function(success) {
// Insert code here.
});
Leanplum.start();
Leanplum.onStartResponse((success: boolean) => {
if (success) {
// handle success
} else {
// handle failure
}
});
When a variable is ready
This callback is executed after start every time, but only after forceContentUpdate if a specific variable has changed (not supported on JavaScript).
//Define the variable and set the name and value using the Var class.
var startLabel = Var(name: "startLabel", string: "Start")
// Then wrap your code in the callback.
startLabel.onValueChanged({
self.startButton.titleLabel.text = self.startLabel.stringValue
// Insert code here.
})
// Define the variable with the correct macro.
DEFINE_VAR_STRING(startLabel, @"Start");
// Then wrap your code in the callback.
[startLabel onValueChanged:^{
self.startButton.titleLabel.text = startLabel.stringValue;
// Insert code here.
}];
// Define the file variable with Var.define.
Var<String> startLabel = Var.define("welcomeMessage", "Start");
// Then add a handler and pass it a new callback.
startLabel.addValueChangedHandler(new VariableCallback<String>() {
public void handle(Var<String> var) {
// Insert code here.
}
});
// Define the variable.
Var<string> startLabel = Var<string>.Define("startLabel", "Start");
// Then, insert your code in a delegate added to ValueChanged.
startLabel.ValueChanged += delegate {
// Insert code here.
};
const func = () => {};
const variableName = 'stringVar';
const value = await Leanplum.onValueChanged(variableName, func);
This callback does not wait for files to finish downloading. If you are using file variables, see below.
When all variables are ready
This callback is executed after start every time, but only after forceContentUpdate if any variable has changed.
You can use forceContentUpdate (except in JavaScript) to re-sync with Leanplum during an active session.
Leanplum.onVariablesChanged { () in
// Insert code here.
}
[Leanplum onVariablesChanged:^() {
// Insert code here.
}];
Leanplum.addVariablesChangedHandler(new VariablesChangedCallback() {
public void variablesChanged() {
// Insert code here.
}
});
Leanplum.VariablesChanged += delegate {
// Insert code here.
};
Leanplum.addVariablesChangedHandler(function(success){
// Insert code here.
});
const func = () => {};
Leanplum.onVariablesChanged(func);
This callback does not wait for files to finish downloading. If you are using file variables, see below.
When a file is ready
This callback will execute after start every time, but only after forceContentUpdate when the file has changed. If the file is unchanged, no download will occur and the callback will not be executed.
//Define the variable and set the name and value using the Var class
var goldstarImage = Var(name: "goldStar", file: "gold_star.png")
goldStar.onFileReady { () in
goldStarImage = UIImage.init(contentsOfFile: (goldStar.fileValue())!)
}
// Define the file variable with the correct macro.
DEFINE_VAR_FILE(goldStar, @"gold_star.png");
[goldStar onFileReady:^() {
goldStarImage = [UIImage imageWithContentsOfFile:[goldStar fileValue]];
}];
// Define the file variable with defineAsset.
Var<String> kitten = Var.defineAsset("kitten", "kitten.jpg");
kitten.addFileReadyHandler(new VariableCallback<String>() {
public void handle(Var<String> variable) {
// Insert code here.
}
});
When all variables and files are ready
You can also wait for all variable changes and file downloads. This callback will execute after start, but only after forceContentUpdate if any of your Leanplum variables or files have changed.
// Add a callback.
Leanplum.onVariablesChangedAndNoDownloadsPending { () in
// Insert code here.
}
// Or, add a responder to be executed as a callback.
Leanplum.addVariablesChangedAndNoDownloadsPendingResponder(self, with: #selector(self.customResponder))
func customResponder() {
// Insert code here.
}
// Add a callback.
[Leanplum onVariablesChangedAndNoDownloadsPending:^() {
goldStarImage = [UIImage imageWithContentsOfFile:[goldStar fileValue]];
}];
// Or, add a responder to be executed as a callback.
[Leanplum addVariablesChangedAndNoDownloadsPendingResponder:self withSelector:@selector(customResponder)];
- (void)customResponder {
// Insert code here.
}
// Add a callback.
Leanplum.addVariablesChangedAndNoDownloadsPendingHandler(new VariablesChangedCallback() {
public void variablesChanged() {
// Insert code here.
}
});
// Add a callback.
Leanplum.VariablesChangedAndNoDownloadsPending += delegate {
// Insert code here.
};
Leanplum.onVariablesChangedAndNoDownloadsPending(() => {
console.log('onVariablesChangedAndNoDownloadsPending');
});
Testing callbacks in development mode
When you're testing your app, it's often useful to tweak values and see the result instantly. If you use Variable callbacks, your variables will change immediately after you change them in the Leanplum dashboard.
If you want to mimic your users' experience and don't want to see real-time changes when testing, use the start callback, which is only triggered the first time variables receive their values on start.
Leanplum will tell you if it couldn't connect to the server, in which case the values will be whatever they were the last time the app was run (or their default values set in the code if the app hasn't been run yet).
Syncing with Leanplum mid-session
Use forceContentUpdate to sync during a user's session, instead of just at session start.
Leanplum’s SDK is designed to sync variables, user attributes, messages, and A/B tests on Leanplum.start, at the beginning of a new session. This ensures users receive the latest updates and messages, and that they are entered into latest A/B tests.
A session starts when a unique user has opened the app and initialized Leanplum (calling Leanplum.start). The session end is defined by 30 minutes or more inactivity after the app is backgrounded. A session will also end if the app remains open but inactive for two or more hours. A force quit of the app (killing the app) also counts as a session end.
By default, we do not sync these values in the middle of a user’s session (after the initial call to start). Our SDK was designed this way to keep it as efficient as possible, requiring only one call to Leanplum’s servers for an entire session, and to keep your app as stable as possible by not altering the app as your users use it.
For most cases, syncing on start is the easiest and best way to keep variables, message targeting and A/B test segments up-to-date.
Using forceContentUpdate
However, there may be some cases where you want to update your messaging or A/B tests mid-session, to be sure a user is appropriately targeted and/or included in a test.
Some examples might be:
- when a user comes back from backgrounding the app (and something may have changed server-side)
- when a user levels up in a game (State or Event change)
- when a user goes from a silver member to gold (User Attribute change)
For these cases, you can force a content update by calling Leanplum.forceContentUpdate(), which syncs variables, user attributes, messages, and A/B tests. Any callbacks you have set up, like onVariablesChanged, will be invoked again automatically.
Calling forceContentUpdate will update all of your variables, user attributes, messages and A/B tests, so it can (depending on how your app is set up to work with Leanplum) affect the functionality and UI of your app. Make sure you consider this when implementing forceContentUpdate.
Event and parameter tracking
Events allow you to track user activity in your app. You can also use events to target users for certain messages, tests, or other content changes. You can verify when an event is tracked in the Debugger or the Events tab in the Leanplum dashboard.
An event is anything that can occur in your app. Events include clicking on a link, sharing, purchasing, killing enemies, etc. All events are timestamped according to when they occur. Thus, it is not advisable to log too many events, as each one will have to be sent to our server.
500 event limit and event naming limits
Note that there is a limit of 500 events per app in Leanplum. Since events are not unlimited, it's best to track more general events, and use parameters to track specific information associated with the event.
For example, you should use a simple name for a purchase event, such as LP_PURCHASE_EVENT and pass a purchase ID or item ID as a parameter. Review our Naming rules for events, states, and parameters for a full list of limitations.
A parameter is a piece of data associated with an event or state. You can supply parameters as a dictionary along with events and states. Here are some reports you can run with parameters:
- Filter reports by event parameter values
- Group metrics by distinct event parameter values (creates a bar graph + table). Example: Show me my top purchased items.
- Group metrics by ranges of event parameter values (creates a histogram + table). Example: Show me the distribution of purchase prices. Example: Show me the distribution of points scored.
- Create custom metrics for numeric parameter values, like totals and averages. Example: For a purchase event, track the average revenue and the amount of currency bought per user.
Parameter limitations
Parameters and Event Values are not available in Developer activity analytics, but you can verify your parameters are being tracked correctly in the Debugger console.
Also, with the out-of-box Leanplum SDK, parameters cannot be used as a criteria to target users. For example, if you have an event "Favorite_Color_Selected" with parameters for each color, you would not be able to target users who completed the Favorite color select event and chose the color blue.
Events and states accumulate over time, and we send events in batches periodically to minimize network usage and maximize battery life.
Tracking an event
Add the following lines of code to track an event. You can place the Leanplum track call anywhere as long as it executes after start (examples below):
// This example tracks the event "Launch"
import UIKit
import Foundation
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(application: UIApplication!, didFinishLaunchingWithOptions launchOptions: NSDictionary!) -> Bool {
...
Leanplum.onVariablesChanged({
Leanplum.track(event: "Launch")
})
...
return true
}
...
// This example tracks the event "Launch" after defining the variable "welcomeMessage".
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{ ...
[Leanplum onVariablesChanged:^{
NSLog(@"%@", welcomeMessage.stringValue);
[Leanplum track:@"Launch"];
}];
return YES;
}
...
@end
// This example tracks the event "Launch" after defining the variable "welcomeMessage".
import com.leanplum.annotations.Variable;
import com.leanplum.annotations.Parser;
public class ApplicationClass extends Application {
public static String welcomeMessage = "Welcome to Leanplum!";
public void onCreate() {
...
Parser.parseVariables(this);
Leanplum.addVariablesChangedHandler(new VariablesChangedCallback() {
public void variablesChanged() {
Log.i("Test", welcomeMessage);
Leanplum.track("Launch");
}
});
...
Leanplum.start();
}
}
// This example tracks the event "Launch".
using LeanplumSDK;
public class LeanplumWrapper : MonoBehaviour
{ ...
void Start()
{ ...
Leanplum.Track("Launch");
}
}
// Tracks view cart event for a user.
Leanplum.track("View Cart");
// Tracks view cart event for a user.
Leanplum.track("View Cart");
Here are some more examples:
// User killed an enemy.
Leanplum.track(event: "Kills")
// User completed a challenge.
Leanplum.track(event: "Score", value: 1)
Leanplum.track(event: "Challenges")
// User liked a post.
Leanplum.track(event: "Likes", info: post.id)
// Or, you can supply a dictionary with up to 200 numerical or string parameters.
Leanplum.track(event: "Likes", params:["post":post.id])
// User killed an enemy.
[Leanplum track:@"Kills"];
// User completed a challenge.
[Leanplum track:@"Score" withValue:@1];
[Leanplum track:@"Challenges"];
// User liked a post.
[Leanplum track:@"Likes" withInfo:@"Post Info"];
// Or, you can supply a dictionary with up to 200 numerical or string parameters.
[Leanplum track:@"Likes" withParameters:@{@"post":post.id}];
// User killed an enemy.
Leanplum.track("Kills");
// User completed a challenge.
Leanplum.track("Score", challengeValue);
Leanplum.track("Challenges");
// User liked a post.
Leanplum.track("Likes", post.id());
// Or, you can supply a dictionary with up to 200 numerical or string parameters.
Map<String, Object> params = new HashMap<String, Object>();
params.put("post", post.id());
Leanplum.track("Likes", params);
// User killed an enemy.
Leanplum.Track("Kills");
// User completed a challenge.
Leanplum.Track("Score", challengeValue);
Leanplum.Track("Challenges");
// User liked a post.
Leanplum.Track("Likes", post.id());
// Or, you can supply a dictionary with up to 200 numerical or string parameters.
Dictionary<string, object> params = new Dictionary<string, object>();
params.Add("post", post.id());
Leanplum.Track("Likes", params);
// You can also pass a value and parameters.
// User made a purchase. Use Leanplum.PURCHASE_EVENT_NAME to indicate a purchase.
Dictionary<string, object> item = new Dictionary<string, object>();
params.Add("itemCategory", "Apparel");
Leanplum.Track(Leanplum.PURCHASE_EVENT_NAME, 19.99, item);
// Tracks view cart event for a user.
Leanplum.track("View Cart");
// Tracks view cart event with numeric event parameter, itemsInCart.
Leanplum.track("View Cart", {itemsInCart: 4});
// Tracks an event with a value and two event parameters.
Leanplum.track("Purchase", 4.99, {itemCategory: 'Apparel', itemName: 'Shoes'});
// Tracks view cart event for a user.
Leanplum.track('View Cart');
// Tracks view cart event with numeric event parameter, itemsInCart.
Leanplum.track('View Cart', {itemsInCart: 4});
// Tracks an event with a value and two event parameters.
Leanplum.track('Purchase', {itemCategory: 'Apparel', itemName: 'Shoes'});
Tracking purchase and monetization events
You can track purchases or other monetization events in Leanplum, which will provide you with revenue metrics in your Analytics reports.
In-app purchases (iOS)
Leanplum supports receipt validation with the App Store and Google Play. On iOS, you can track in-app purchases automatically. Simply add this line of code before Leanplum starts:
Leanplum.trackInAppPurchases()
[Leanplum trackInAppPurchases];
//For iOS
Leanplum.TrackIOSInAppPurchases();
//For Android see Android-specific instructions below.
Leanplum.trackInAppPurchasesIos()
Leanplum will also convert all purchases to USD. To require valid receipts upon purchase or change your preferred currency, update your preferences in App settings in the dashboard.
To reach your currency settings, click your name in the upper right corner of the dashboard , then select App settings. Next, select "Keys & Settings" next to your app to open this dialogue.
Android in-app purchases
If your app uses Google Play In-App Billing, Leanplum can automatically track those purchases and validate receipts with the Google Play store. First, provide us with your Google Play license key. Next, add the following code sample in your class that implements ``com.android.vending.billing.util.IabHelper.OnIabPurchaseFinishedListener:
import org.json.JSONException;
import org.json.JSONObject;
import android.util.Log;
import com.android.vending.billing.util.IabHelper.QueryInventoryFinishedListener;
import com.android.vending.billing.util.IabResult;
import com.android.vending.billing.util.Inventory;
import com.android.vending.billing.util.Purchase;
import com.android.vending.billing.util.SkuDetails;
import com.leanplum.Leanplum;
...
public void onIabPurchaseFinished(IabResult result, Purchase info) {
final Purchase purchase = info;
billingHelper.queryInventoryAsync(new QueryInventoryFinishedListener() {
public void onQueryInventoryFinished(IabResult result, Inventory inv) {
// Track in-app purchase.
SkuDetails skuDetails = inv.getSkuDetails(purchase.getSku());
try {
JSONObject skuData = new JSONObject(skuDetails.toString()
.substring(skuDetails.toString().indexOf(":") + 1));
Leanplum.trackGooglePlayPurchase(
skuData.getString("title"), // Alternatively, skuData.getString("productId"), if you like.
skuData.getLong("price_amount_micros"),
skuData.getString("price_currency_code"),
purchase.getOriginalJson(), purchase.getSignature()
/* optionally supply event parameters as an additional argument */);
} catch (JSONException e) {
Log.e("Leanplum", "Cannot get purchase price from improperly formatted SKU");
}
// Code to consume purchase.
// It's important that you do not consume while querying inventory, or you'll receive
// an IllegalStateException.
// billingHelper.consumeAsync(purchase, null);
}
});
}
Manually tracking monetization events
You can manually track your monetization event within the app. To do so you have to pass a purchase event name, a purchase value, the currency code, and parameters with info about the purchase event with each purchase (all the parameters are required).
The purchase event name is defined by default as “Purchase”. Based on the currency code being passed, the value will be converted to USD automatically in the Dashboard.
Leanplum.track(event: LP_PURCHASE_EVENT, value: 5.0, currencyCode: "EUR", params: ["serial" : 12345, "name":"coffee"])
[Leanplum trackPurchase:LP_PURCHASE_EVENT withValue:5.0 andCurrencyCode:@"EUR" andParameters:@{@"serial": @12345, @"name": @"coffee"}];
Map<String, Object> purchaseParams = new HashMap<String, Object>();
...
purchaseParams.put("Item code", 12345);
purchaseParams.put("Name", "Coffee");
Leanplum.trackPurchase(Leanplum.PURCHASE_EVENT_NAME, 5, "EUR", purchaseParams);
// Leanplum JS SDK 1.5.0+
// Leanplum.trackPurchase(value, currencyCode, purchaseParams, purchaseEvent)
Leanplum.trackPurchase(5.00, 'EUR', { serial: 12345, name: 'coffee' })
Leanplum.trackPurchase(value, currencyCode, purchaseParams, purchaseEvent)
Other monetization events
You can also use the standard event tracking for monetization events as well. It works like any other event — just use "Purchase" as the event name and a value.
The only difference is that unlike using trackPurchase, the normal Leanplum.track call will not automatically convert different currencies to USD.
Leanplum.track(event: LP_PURCHASE_EVENT, value:19.99)
[Leanplum track:LP_PURCHASE_EVENT withValue:19.99];
Leanplum.track(Leanplum.PURCHASE_EVENT_NAME, 19.99, item);
Leanplum.track('Purchase', 19.99, { itemsInCart: 4 });
Leanplum.track(PURCHASE_EVENT_NAME, {itemsInCart: 4});
The value should be the revenue for the transaction in a common currency.
If you prefer to use a different event name than "Purchase", you can choose a different name. Select the + tile on the Analytics page for a new metric. In the dialogue box that opens, select the Monetization category and edit the purchase event. The changes will be applied retroactively.
Whether you choose your own name for purchase events or use the default LP_PURCHASE_EVENT, make sure to stay consistent and always use the same event name.
State tracking
A state is any part of your app a user can be in. For example, some states can include being in a particular level, watching a video, or browsing an in-app store.
All states have a time and a duration. The duration is set automatically — when one state begins, the previous one ends.
Advancing to a state
This example is called when the user advances to the next level.
Leanplum.advance(state: "Level", info: level.name)
[Leanplum advanceTo:@"Level" withInfo:level.name];
Leanplum.advanceTo("Level", level.name());
Leanplum.AdvanceTo("Level", level.Name);
// Tracks a state with a numeric parameter.
Leanplum.advanceTo("Cart", {numItems: 2});
Leanplum.advanceTo("Cart", "info", {numItems: 2});
Pausing and resuming
This is useful if your game has a "pause" mode. You shouldn't call it when someone switches out of your app because that's done automatically.
Leanplum.pauseState()
Leanplum.resumeState()
[Leanplum pauseState];
[Leanplum resumeState];
Leanplum.pauseState();
Leanplum.resumeState();
Leanplum.PauseState();
Leanplum.ResumeState();
Leanplum.pauseState();
Leanplum.resumeState();
Leanplum.pauseState();
Leanplum.resumeState();
The nil / null state
This state causes the user to leave the current state and not enter another one.
Leanplum.advance(state:nil)
[Leanplum advanceTo:nil];
Leanplum.advanceTo(null);
Leanplum.AdvanceTo(null);
Leanplum.advanceTo(null);
Leanplum.advanceTo(null);
User and device tracking
When Leanplum starts for the first time in your app, it creates a new user profile or User ID. Unless you set up your own concept of User IDs, the UserID value will be identical to the DeviceID value, and Leanplum will start tracking sessions for this user.
Sessions are how Leanplum organizes each user's events, states, and other general activity.
See below for more on handling user IDs, devices, and sessions.
Device IDs
The device ID uniquely identifies the devices and is determined automatically by the SDK. See below for details on iOS and Android device IDs.
If you plan on using external attribution services, make sure the device ID you set matches the device ID sent by your attribution provider (e.g. IDFV -> IDFV). See How to integrate external Attribution services for more.
iOS device ID
On iOS, by default, we use the identifierForVendor. If the device is pre-iOS 6, we use a hash of the MAC address, and in development mode, we use the advertising identifier.
You can choose how the device ID is set the first time start is called on that device by calling one of these before start:
LEANPLUM_USE_ADVERTISING_ID: a macro that uses the advertising identifier.[Leanplum setDeviceId:@"customAndUniqueId"]: Sets the device ID to a custom ID. Make sure that your custom ID is unique per device.
The deviceId is set when Leanplum start runs for the first time on that device. After this, it cannot be changed unless the user completely uninstalls and reinstalls your app.
Android device ID
On Android, by default, we use an MD5 hash of the MAC address if the user is on a version prior to Marshmallow (Android 6) and if your app has ACCESS_WIFI_STATE permissions. Otherwise, we use the ANDROID_ID.
You can choose how the device ID will be set the first time start is called on that device by calling one of these before start:
Leanplum.setDeviceIdMode(LeanplumDeviceIdMode.ANDROID_ID)Uses theANDROID_ID.Leanplum.setDeviceIdMode(LeanplumDeviceIdMode.ADVERTISING_ID)Uses advertising ID if available; otherwise, uses the MD5 hash of the MAC address if the user is on a version prior to Marshmallow (Android 6) and your app hasACCESS_WIFI_STATEpermissions. Otherwise, we use theANDROID_ID.Leanplum.setDeviceId("customAndUniqueId")Sets the device ID to a custom ID. Make sure that your custom ID is unique per device.
The deviceId is set when Leanplum start runs for the first time on that device. After this, it cannot be changed unless the user completely uninstalls and reinstalls your app.
Whether or not you set a DeviceIdMode, the SDK uses the following logic to set the deviceId (unless you use setDeviceId):
- If
DeviceIdModeis set toADVERTISING_ID, useADVERTISING_IDif available. Otherwise, continue. - If
DeviceIdModeis set toANDROID_ID, useANDROID_IDif available. Otherwise, continue. - If a MAC address is available (Android < 6.0 and
ACCESS_WIFI_STATEpermission), use a hash of the MAC address. Otherwise, continue. - If
ANDROID_IDis available, useANDROID_ID. Otherwise, continue. - Otherwise, use a randomly generated device ID.
You can view the source code here.
Unity device ID
The device ID uniquely identifies the devices and is determined automatically by the SDK. On Unity, we use SystemInfo.deviceUniqueIdentifier to get the device ID. Refer to the Unity documentation to learn more.
JavaScript (HTML5) device ID
The device ID uniquely identifies the devices and is determined automatically by the SDK. In the JavaScript SDK, we generate a unique device ID from a random selection of numbers and letters totaling 16 characters. We take that device ID and persist it using localStorage. You can set a custom device ID instead using Leanplum.setDeviceId.
React Native device ID
The device ID uniquely identifies the devices and is determined automatically by the SDK. In the React Native SDK, we use the platform-specific logic for iOS or Android as covered in detail above. We take that device ID and persist it using AsyncStorage. You can set a custom device ID instead using Leanplum.setDeviceId.
User IDs
When Leanplum start is called for the first time on a device, a new user profile is created and Leanplum starts tracking activity and sessions for this user. If no custom User ID is sent with the start call, the User ID value is set to the Device ID.
If you have your own concept of User IDs, you can pass a User ID in the start call. This way, if the user has multiple devices, we will count them as the same user and merge their profiles.
Leanplum.start(userId: "user1234")
[Leanplum startWithUserId:@"user1234"];
Leanplum.start(this, "user1234");
Leanplum.Start("user1234");
Leanplum.start('user1234');
// Start with user ID and attributes.
Leanplum.start('user1234', {'gender': 'Female'});
In some cases, you can also set the User ID after start. See more below:
Logins
Passing a user ID with start may not work well if you have a login system. You should still call start early on to track user activity, then set the user ID later on with setUserId when the user logs in.
Leanplum.setUserId("user1234")
[Leanplum setUserId:@"user1234"];
Leanplum.setUserId("user1234");
Leanplum.SetUserId("user1234");
Leanplum.setUserId("user1234");
// Set user attributes and ID at the same time.
Leanplum.setUserAttributes('user1234', {'gender': 'Female'});
Leanplum.setUserId("user1234");
When the user ID is set for the first time on a device, the existing profile in Leanplum is updated with that user ID and all previously tracked data remains.
After the first setUserId call, each subsequent call that includes a different ID will end the current User session, and create a new session for the new User. If the new User ID doesn't exist, a new User Profile will be created in Leanplum.
Here's how setting the user ID with setUserId works with typical registration and login scenarios:
- Register: If a user ID has not been set on this device yet and the supplied user ID does not exist, Leanplum will update the current user profile (created on
start) with the supplied user ID (replacing the device ID). - Login: If a user ID has not been set on this device yet and the supplied user ID does exist, the current and existing user profiles will be merged. This ensures that users with multiple devices are tracked as one user. If the same user logs back in on this device, no changes will be made to their profile since their user ID is already set.
- Switch user: If a user ID has been set on this device and the supplied user ID is different, the current session will be ended and a new session will be started for the supplied user ID. A user with the supplied user ID will be created if one does not already exist.
Logouts
Leanplum will not end the session after a user logs out and does not include any methods to do so. All user activity is tracked and attributed to the last logged-in user (set by the setUserId call). This allows you to track activity in your app even while the user is logged out.
If you want to keep track of which users are logged in and which are logged out, set a user attribute (e.g. logged_in).
Do not set a different user ID to handle logouts. This will create a new user profile in Leanplum and start a new session for them, which will skew your analytics.
User attributes
A user attribute is any piece of data associated with a user that you provide to the SDK. Each session has its own user attributes, but they get copied from one session to the next. This is in contrast to event parameters, which may take on different values per event. For this reason, you generally use user attributes for things that do not change much within the session, or with which you want the entire session associated.
Uses:
- Personalizing content (variables, messages, resources, and interfaces) to different types of users.
- Targeting an A/B test.
- Filtering reports by a particular user attribute, like only looking at data for "whales".
- Grouping reports (constructing a bar graph or histogram), by different attribute values. E.g. Create a histogram of average session length by number of friends.
Examples:
- Gender
- Age
- Number of friends
- User interests
- Email (required for running email campaigns)
Constraints:
- Up to 200 attributes can be defined for your app.
- Attribute names must be strings, and values must be strings or numbers.
- Attribute values will be the same across all events and states in a particular session.
User attribute as timestamp
Since the only supported formats are string and number, if you wish to save a timestamp, you can do it either in the following number formats:
- Epoch UNIX timestamp - 1555555555
- YYYYMMDD - 20201231
- YYYMMDDHHMMSS - 20201231235959
This will allow you fo perform mathematical operations in the user segmentation and message targeting, such as </> (before/after) or between 2 timestamps.
To schedule campaigns based on a user attribute timestamp, please see this article.
User attribute as list
Since user attribute values can only be strings or numbers, saving an array/list will need to be converted to a string first. This will allow you to:
- Add/remove elements through our setUserAttributes API, using userAttributeValuesToAdd and userAttributeValuesToRemove
- Loop through or use specific array elements with our Templating Language
// Passing attributes at session start allows us to target content based on the attributes.
Leanplum.start(attributes: ["gender":"Female", "age": 29])
// You can also pass them later on in the session, but you won't be able to
// target variables or messages at these for that session.
Leanplum.setUserAttributes(["gender":"Female", "age": 29])
// Clear an attribute.
Leanplum.start(userAttributes: ["gender":NSNull()])
// To allow targeting the user in email campaigns, set the "email" attribute
Leanplum.setUserAttributes(["email":"[email protected]"])
// Passing attributes at session start allows us to target content based on the attributes.
[Leanplum startWithUserAttributes:@{@"gender": @"Female", @"age": @29}];
// You can also pass them later on in the session, but you won't be able to
// target variables or messages at these for that session.
[Leanplum setUserAttributes:@{@"gender": @"Female", @"age": @29}];
// Clear an attribute.
[Leanplum startWithUserAttributes:@{@"gender": [NSNull null]}];
// To allow targeting the user in email campaigns, set the "email" attribute
[Leanplum setUserAttributes:@{@"email": @"[email protected]"}];
// Passing attributes at session start allows us to target content based on the attributes.
Map<String, Object> attributes = new HashMap<String, Object>();
attributes.put("gender", "Female");
attributes.put("age", 29);
Leanplum.start(this, attributes);
// You can also pass them later on in the session, but you won't be able to
// target variables or messages at these for that session.
Leanplum.setUserAttributes(attributes);
// Clear the attributes.
attributes.put("gender", null);
attributes.put("age", null);
Leanplum.setUserAttributes(attributes);
// To allow targeting the user in email campaigns, set the "email" attribute
attributes.put("email", "[email protected]");
Leanplum.setUserAttributes(attributes);
// Passing attributes at session start allows us to target content based on the attributes.
Dictionary<string, object> attributes = new Dictionary<string, object>();
attributes.Add("gender", "Female");
attributes.Add("age", 29);
Leanplum.Start(attributes);
// You can also pass them later on in the session, but you won't be able to
// target variables or messages at these for that session.
Leanplum.SetUserAttributes(attributes);
// Clear the attributes.
attributes.Add("gender", null);
attributes.Add("age", null);
Leanplum.SetUserAttributes(attributes);
// To allow targeting the user in email campaigns, set the "email" attribute
attributes.Add("email", "[email protected]");
Leanplum.SetUserAttributes(attributes);
// Passing attributes at session start allows us to target content based on the attributes.
var attributes = {'gender': 'Female', 'age': 29};
Leanplum.start(attributes);
// You can also pass them later on in the session, but you won't be able to
// target variables or messages at these for that session.
Leanplum.setUserAttributes(attributes);
// Clear the attributes.
Leanplum.setUserAttributes({'gender': null, 'age': null});
// To allow targeting the user in email campaigns, set the "email" attribute
Leanplum.setUserAttributes({'email': '[email protected]'})
Leanplum.setUserAttributes({'gender': 'Female'});
// Examples
var attributes = {'gender': 'Female', 'age': 29};
Leanplum.setUserAttributes(attributes);
// Clear the attributes
Leanplum.setUserAttributes({'gender': 'Male', 'age': 30});
// To allow targeting the user in email campaigns, set "email" attribute
Leanplum.setUserAttributes({'email': '[email protected]'})
Sessions
How the Leanplum SDK tracks sessions
Once you complete our initial SDK setup, Leanplum will track your users' sessions data automatically.
Session start
A session starts when a unique user has opened the app and initialized Leanplum (calling Leanplum.start).
Session pause and resume
When the app is backgrounded, Leanplum will pause the session. When the app is re-opened, the session will resume. If the pause (background) lasts over 30 minutes, resuming the session will count as a new session.
See resumeSession for more on resume.
Session end
The session end is defined by 30 minutes or more inactivity after the app is backgrounded. If a user re-opens the app after backgrounding for 30 or more minutes, Leanplum will count this as a new session.
A session will also end if the app remains open but inactive for 2 or more hours. Force quitting the app (killing the app) also counts as a session end.
Session tracking in Analytics
Session info gets sent to Analytics as soon as the session ends, though it may take between 2-4 hours to see results come in. Note that sessions lasting longer than 8 hours will not count towards any duration statistics in Analytics — this is designed to prevent your duration results from being skewed by just a few outliers.
Offline event tracking
If events or states are tracked while a session is paused or closed, Leanplum will still track these events as part of an offline session. Events and states tracked during offline sessions will still count in Analytics.
Also note that if there are over 1500 events in a single session, the Analytics dashboard will truncate events over 1500. You can, however, view the full number of events in the user's profile in these cases.
See API limits for more information on our limits.
HTML5 Sessions tracking
Note that the JavaScript SDK tracks sessions differently than our mobile SDKs.
Our web SDK is more flexible in how you can manage your sessions and timestamps. By default, every time a new tab or window opens with your app, a new session will begin.
Depending on how long it has been since the user opened the app, you may want to track these new tabs as part of a single session. In order to do so, call the useSessionLength() method before calling start():
Leanplum.useSessionLength(2 * 60 * 60); // 2 hours (in seconds)
Leanplum.start();
Calling this method stores the time of the last successful call in the browser localStorage. If the last call to start() has occurred in less than this time, the session will be restored from cache by calling the startFromCache() method internally.
In-app messaging
Leanplum comes with a number of in-app message templates, including alerts, confirmation messages, popups, and full-screen interstitials. They are all open source, so you can customize them however you want, and you can even create your own templates.
To enable in-app messaging, you'll need to integrate the Leanplum SDK and run your app at least once in development mode.
Once you finish the SDK setup, you can trigger in-app messages to send after an event or when the app starts. Any trigger events must be tracked within our SDK, but there's no additional coding needed. Note that currently, In-app messages cannot be triggered using API calls, only from the SDK.
Android in-app message deferral
By default, In-App messages are attached to the current activity. If the current activity is a splash screen or some other temporary screen, the in-app message will also disappear once the current activity is dismissed. For these situations, we have the LeanplumActivityHelper.deferMessagesForActivities method, which allows you to specify activities where In-App messages should not be displayed. When an activity is on the deferred list, the in-app message will be displayed on the first available activity that is not contained in the list.
To use LeanplumActivityHelper.deferMessagesForActivities your Android SDK version needs to be at least 1.3.1 and you need to add it before Leanplum.start as follows: LeanplumActivityHelper.deferMessagesForActivities(SplashActivity.class);
Customizing in-app message templates
Our in-app messaging templates are open-source, which means you can modify them, delete them, or create your own. The Leanplum dashboard will reflect your new templates the next time you run your app and sync the templates.
Once the templates are created or customized, they can be synced with Leanplum, just like Variables, by running the app in Development mode on a registered test device. The process of customizing these templates differs slightly depending on the platform:
iOS custom templates
The files LPMessageTemplates.h and LPMessageTemplates.m come with the SDK. Just download the SDK, unzip, and put the template files in your project. Then, initialize them before the [Leanplum start] call with:
[LPMessageTemplates sharedTemplates];
Take a look at LPMessageTemplates.m to see how the messages are implemented.
To define a new template, use the method:
[Leanplum defineAction:ofKind:withArguments:withResponder:]
Here's how it works:
- defineAction: The name of the action or message type you are defining.
- ofKind: One or more action kinds OR'ed together. kLeanplumActionKindMessage will appear in the Message Type list for creating message campaigns. kLeanplumActionKindAction will appear in the dropdown when choosing an action within a message.
- withArguments: A list of LPActionArg objects, with each one being an argument that the marketer can fill out on the dashboard. For example,
Title,Message Text, orColor. Action argument names need to end with lowercaseaction, for exampleAccept action. - withResponder: A block that accepts an LPActionContext and returns a BOOL whether the action was handled or not. You may decide not to handle the action based on some additional logic defined in the responder. From the context, you can access the values for any argument you defined in the template, as well as some other special methods:
[LPActionContext runActionNamed:]: Runs another action when this action is done. The action name should correspond to a named LPActionArg of kind Action.[LPActionContext runTrackedActionNamed:]: Runs another action and tracks an event that the action occurred.[LPActionContext track:withValue:andParameters:]: Tracks an event associated with the current action.[LPActionContext muteFutureMessagesOfSameKind]: Don't show the current message campaign again on this device. For example, if you have a survey popup every 10th session, you may want the ability for the user to decide to remind them later, which simply dismisses the message, or the option to never see this survey again.
iOS sample project: custom template
Take a look at the sample project here.
In this sample, we are adding a new in-app message template — specifically, a three-button Confirm message.
First, open the AppDelegate.m and import the LPMessageTemplateClass. Then put [LPMessageTemplates sharedTemplates] before [Leanplum start];
OpenLPMessageTemplates.m and check in the sample where the "#### example" comments are placed — you'll find the code parts being added to create the new in-app message template and comments describing each code part being added.
Next, build and run the project. If the device is registered as a Test Device, the three-button message will be added to the available in-app templates in the Dashboard. You may also have to hit the 'sync templates' button in the dashboard to see the final template.
Android custom templates
First, download the Leanplum Android SDK zip archive, if you haven't already.
Unzip the archive and look for src/com/leanplum/messagetemplates. Add all of these files to your project, preserving the directory structure com/leanplum. Use a different package name (for example, custommessagetemplates) so it does not collide with the default one.
Initialize them in your Application class (and before calling Leanplum.start()):com.leanplum.custommessagetemplates.MessageTemplates.register(getApplicationContext());
Look at the provided files to see how the messages are implemented. To define a new template, use the method Leanplum.defineAction(). Here's how it works:
- name: The name of the action or message type you are defining.
- kind: One or more action kinds OR'ed together. Leanplum.ACTION_KIND_MESSAGE will appear in the Message Type list for creating message campaigns. Leanplum.ACTION_KIND_ACTION will appear in the dropdown when choosing an action within a message.
- args: An ActionArgs instance, defining the options that the marketer can fill out on the dashboard. For example,
Title,Message Text, orColor. Action argument names need to end with lowercaseaction, for exampleAccept action. - responder: A callback that accepts an ActionContext and returns a boolean whether the action was handled or not. You may decide not to handle the action based on some additional logic defined in the responder. From the context, you can access the values for any argument you defined in the template, as well as some other special methods:
ActionContext.runActionNamed(): Runs another action when this action is done. The action name should correspond to a named action argument defined within ActionArgs.ActionContext runTrackedActionNamed(): Runs another action and tracks an event that the action occurred.ActionContext.track(): Tracks an event associated with the current action.ActionContext.muteFutureMessagesOfSameKind(): Don't show the current message campaign again on this device. For example, if you have a survey popup every 10th session, you may want the ability for the user to decide to remind them later, which simply dismisses the message, or the option to never see this survey again.
Android sample project: custom template
The project sample can be found here.
In this sample we are also adding a new In-App message — specifically, a three-button Confirm message.
Download the Android SDK from the Leanplum SDK Setup page and extract the archive. com/leanplum/messagetemplates has been copied from the Android SDK folder and added to the project and in the Application class. Before Leanplum.start, com.leanplum.custommessagetemplates.register(getApplicationContext()); is placed.
Unlike the iOS Message Templates, every template is included in its own class in Android.
Create a new class specific to the new message template. In the sample, we've called it 'Confirm3Buttons'. The class name will also be the new template's name in the Leanplum dashboard.
Open the Confirm3Buttons class and check for the "#### example" comments — you'll find the code parts being added to create the new in-app message template and comments describing each code part being added.
The last step is to register the newly added in-app message. Open the MessageTemplates class and add Confirm3Buttons.register(currentContext); in the 'register' function.
To sync the new template to the Leanplum dashboard, run the project on a registered test device. You may also have to hit the 'sync templates' button in the dashboard to see the final template.
Sending iOS 10.3+ app review requests
With iOS 10.3, Apple changed how apps can request reviews from users. Per their release notes, developers must now use the SKStoreReviewController API to ask users to rate or review the app while they're using it, without being redirected to the App Store. This is part of a larger overhaul of Apple's ratings and reviews.
To send an app review request on Android or iOS 10.2-, see Send an app review request (Android or iOS 10.2 and earlier).
You can now control when in your app to present the review prompt by calling the API in your app's code, or by using Leanplum's in-app messaging templates — follow the steps below to set up the new App review request.
Update Xcode and your pods/dependencies
Before you can take advantage of the new features in iOS 10.3, you'll need to update Xcode and update your pods/dependencies to get our latest SDK version (requires 1.7.0+).
- Update Xcode to version 8.3.
- Update your pods to Leanplum iOS SDK 1.7.0+. From the terminal or command prompt, go to your project's root directory and run: pod update
- Confirm in the pod notes that Leanplum SDK 1.7.0+ has been installed.
- Build and run your app using a registered test device.
If you do not see the "Request App Rating" under In-App Actions, you may need to click the refresh button at the top of the In-app templates dropdown menu, or refresh the page in your browser.
Our dashboard will not show the new in-app message template for app reviews until you run a test device built from Xcode 8.3 with the Leanplum iOS SDK 1.7.0+. If your test devices use older versions of our SDK, the dashboard will not display features exclusive to 1.7.0+.
Resubmit your app to the App Store
Once you've finished making the above changes and testing locally, you'll need to resubmit your app to the App Store. You can then start testing App Review prompts using Leanplum.
Web In-App messages
Showing Web Popups configured through the Leanplum Dashboard
Starting with the Leanplum JavaScript SDK 1.7.0, you can show and track In-App messages in web applications. To start consuming In-App messages in the JS SDK, you need to write code that renders the In-App Messages JSON as a popup and tracks message actions accordingly.
Beta functionality
This functionality is in beta stage of development. Some functionality available in the Leanplum Dashboard will have no effect in the SDK, and the API does not fully match the iOS and Android counterparts.
Missing functionality:
- Geo-fencing messages ("User enters / exits custom region")
- API for muting campaign messages (
muteFutureMessagesOfSameKindmethod) - The "Register for Push" app function
- User attribute triggers that use the "changes from value to value" trigger
Starting with the Leanplum JavaScript SDK 1.8.0, you can enable automatic display of rich in-app messages.
Leanplum.enableRichInAppMessages(true);
To handle custom in-app message templates, as well as the non-rich in-app messages, you can define a JavaScript code that shows and tracks them.
The following sample shows how to show and track an In-App Confirm message.
// 1. Register handler for in-app messages
Leanplum.on('showMessage', function (args) {
var message = args.message;
var context = args.context;
// 2. Filter out unsupported message types
if (message.__name__ !== 'Confirm') {
return;
}
// 3. Track impression
context.track();
// 4. Show message and trigger attached actions
// (message can be rendered in HTML and actions can be called asynchronously)
if (confirm(message.Message)) {
context.runTrackedActionNamed('Accept action');
} else {
context.runTrackedActionNamed('Cancel action');
}
});
The showMessage event arguments contains two objects:
message- a Message object, specific to each type of message, as listed belowcontext- an ActionContext object, which is used for tracking user actions and triggering actions
Each Message has a __name__ field that indicates the type of the message (and the shape of the default JSON object). The following list shows the JSON object for each type of message.
Available actions: Available actions: Available actions: Available actions: none Available actions: Available actions: Confirm
{
__name__: "Confirm",
Title: "Would you like to save $10?",
Message: "This action cannot be undone.",
"Accept text": "Yes",
"Cancel text": "No"
}Accept action, Cancel actionCenter Popup
{
__name__: "Center Popup",
Layout: {
Height: 250,
Width: 300
},
"Background color": "rgba(255,255,255,1)",
"Background image": "",
Title: {
Color: "rgba(0,0,0,1)",
Text: "Would you like to save 10%?"
},
Message: {
Color: "rgba(0,0,0,1)",
Text: "Use the code 'SAVE10' at checkout!"
},
"Accept button": {
"Background color": "rgba(255,255,255,1)",
Text: "Go to checkout",
"Text color": "rgba(0,0,0,1)"
}
}Accept actionAlert
{
__name__: "Alert",
Title: "Raffle entry successful.",
Message: "You have entered our raffle!"
}Dismiss actionWeb Interstitial
{
__name__: "Web Interstitial",
URL: "https://example.com",
"Has dismiss button": true
}Push Ask to Ask
{
__name__: "Push Ask to Ask",
Layout: {
Height: 250,
Width: 300
},
"Background color": "rgba(255,255,255,1)",
"Background image": "",
Title: {
Color: "rgba(0,0,0,1)",
Text: "LeanplumSample"
},
Message: {
Color: "rgba(0,0,0,1)",
Text: "Tap OK to receive important notifications from our app."
},
"Accept button": {
"Background color": "rgba(255,255,255,1)",
Text: "OK",
"Text color": "rgba(0,0,0,1)"
},
"Cancel button": {
"Background color": "rgba(255,255,255,1)",
Text: "Remind me later",
"Text color": "rgba(0,0,0,1)",
}
}Accept action, Cancel actionInterstitial
{
__name__: "Interstitial",
"Background color": "rgba(255,255,255,1)",
"Background image": "",
Title: {
Color: "rgba(0,0,0,1)",
Text: "Interstitital Test"
},
Message: {
Color: "rgba(0,0,0,1)",
Text: "Interstitial Message Body"
},
"Accept button": {
"Background color": "rgba(255,255,255,1)",
Text: "Accept",
"Text color": "rgba(0,0,0,1)"
}
}Accept action
Push notifications
Setting up push notifications with Leanplum allows you to send notifications through the Leanplum dashboard or with the API. In the dashboard, you'll be able to customize the content of your push notifications and create campaigns that combine push with other messaging channels.
Select your OS or language below for specific instructions on setup and other push-related features.
iOS
More on setup, iOS notification types, badge counts, and more.
Android
More on setup and customizing android notifications.
Unity
More on setup for iOS and Android notifications.
JavaScript
More on web push notifications using our HTML5 SDK.
React Native
More on setup notifications for React Native.
iOS push notifications
iOS push setup, categories, badge counts, alerts, and testing
Leanplum is serving iOS push notifications by natively using Apple Push Notification Service (APNS). To enable Push Notifications on iOS, you need to upload your certificates to Leanplum and register for remote notifications in your app.
We use swizzling to collect push tokens. If your app has other SDKs installed that also use method swizzling it may cause conflicts. See here for manual instructions.
iOS push setup
- Login to the iOS provisioning portal.
- In the Identifiers > App IDs, select your app, click Edit, and enable Push Notifications.
- Click Create Certificate for each of the Development and Production certificates and follow the onscreen instructions. You should not reuse existing certificates so that we can track delivery failures properly. For Sandbox generate a Sandbox certificate, for Production a Production/Sandbox combined certificate. Apple does not offer the option of Production only certificates anymore.
- Download your new certificate files from your browser. Open the files on your computer, which will launch Keychain.
- In Keychain, select the new certificates, expand them to view the private key, and then right click to export them as .p12 files. You must enter a password.
- In Leanplum, go to your app's Keys & Settings (App Settings > {Your app} > Keys & Settings). Under Push Notifications, upload your .p12 files to Leanplum and enter your passphrase from step 5 above.
- Configure your app to use push notifications in your app delegate's
applicationDidFinishLaunchingmethod. Example:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
//iOS-10
if #available(iOS 10.0, *){
let userNotifCenter = UNUserNotificationCenter.current()
userNotifCenter.requestAuthorization(options: [.badge,.alert,.sound]){ (granted,error) in
//Handle individual parts of the granting here.
}
UIApplication.shared.registerForRemoteNotifications()
}
//iOS 8-9
else if #available(iOS 8.0, *){
let settings = UIUserNotificationSettings.init(types: [UIUserNotificationType.alert,UIUserNotificationType.badge,UIUserNotificationType.sound],
categories: nil)
UIApplication.shared.registerUserNotificationSettings(settings)
UIApplication.shared.registerForRemoteNotifications()
}
//iOS 7
else{
UIApplication.shared.registerForRemoteNotifications(matching:
[UIRemoteNotificationType.alert,
UIRemoteNotificationType.badge,
UIRemoteNotificationType.sound])
}
//Other code.
}
//Note: You can choose any combination of formats — this is just an example.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
id notificationCenterClass = NSClassFromString(@"UNUserNotificationCenter");
if (notificationCenterClass) {
// iOS 10.
SEL selector = NSSelectorFromString(@"currentNotificationCenter");
id notificationCenter =
((id (*)(id, SEL)) [notificationCenterClass methodForSelector:selector])
(notificationCenterClass, selector);
if (notificationCenter) {
selector = NSSelectorFromString(@"requestAuthorizationWithOptions:completionHandler:");
IMP method = [notificationCenter methodForSelector:selector];
void (*func)(id, SEL, unsigned long long, void (^)(BOOL, NSError *__nullable)) =
(void *) method;
func(notificationCenter, selector,
0b111, /* badges, sounds, alerts */
^(BOOL granted, NSError *__nullable error) {
if (error) {
NSLog(@"Leanplum: Failed to request authorization for user "
"notifications: %@", error);
}
});
}
[[UIApplication sharedApplication] registerForRemoteNotifications];
} else if ([[UIApplication sharedApplication] respondsToSelector:
@selector(registerUserNotificationSettings:)]) {
// iOS 8-9.
UIUserNotificationSettings *settings = [UIUserNotificationSettings
settingsForTypes:UIUserNotificationTypeAlert |
UIUserNotificationTypeBadge |
UIUserNotificationTypeSound categories:nil];
[[UIApplication sharedApplication] registerUserNotificationSettings:settings];
[[UIApplication sharedApplication] registerForRemoteNotifications];
} else {
// iOS 7 and below.
[[UIApplication sharedApplication] registerForRemoteNotificationTypes:
UIUserNotificationTypeSound | UIUserNotificationTypeAlert | UIUserNotificationTypeBadge];
}
// Other code.
}
//Note: You can choose any combination of formats — this is just an example.
Note that you don't have to add this if you plan on using the Push Pre-Permission message, which will bring up the iOS prompt for you. You can also choose to bring up the prompt later in the app.
If you are using UNUserNotification.framework, make sure you implement UNUserNotificationCenterDelegate and its methods, to be able to receive events and actions when user opens the app from status bar, or when push notification arrives when app is in foreground.
import UserNotifications
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
UNUserNotificationCenter.current().delegate = self
return true
}
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
}
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
}
}
@interface AppDelegate ()
@end
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
[UNUserNotificationCenter currentNotificationCenter].delegate = self;
return YES;
}
-(void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)(void))completionHandler
{
}
- (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler
{
}
@end
If you are implementing application:didReceiveRemoteNotification:fetchCompletionHandler in your code, you should call the completion handler yourself.
Push categories
iOS 8 supports push notification categories, which allow you to provide interactivity to your notifications with custom actions. To use this, define categories in your code — check out Apple's dev docs.
Leanplum allows you to choose the category of your notifications from the dashboard, track each custom action, and even define the logic for what happens on each action. To use categories with Leanplum, you'll need additional calls to Leanplum to handle custom actions in your app delegate. If you call [Leanplum handleActionWithIdentifier], do not call completionHandler as Leanplum will call this internally when it's ready.
UNNotificationCategory* generalCategory = [UNNotificationCategory
categoryWithIdentifier:@"GENERAL"
actions:@[]
intentIdentifiers:@[]
options:UNNotificationCategoryOptionCustomDismissAction];
// Register the notification categories.
UNUserNotificationCenter* center = [UNUserNotificationCenter currentNotificationCenter];
[center setNotificationCategories:[NSSet setWithObjects:generalCategory, nil]];
let generalCategory = UNNotificationCategory(identifier: "GENERAL",
actions: [],
intentIdentifiers: [],
options: .customDismissAction)
// Register the category.
let center = UNUserNotificationCenter.current()
center.setNotificationCategories([generalCategory])
It's a good practice to show additional information or navigate to the correct part of your app when users open your push notifications. You can do that using Leanplum's in-app messaging, which integrates nicely with push notifications. There's no additional coding needed.
Extended example - Push categories and custom actions
In the steps below, you will find more information on how to set up custom categories and use custom actions on iOS.
The Push Notification's Category is defined in the app code and set in the dashboard Push message under iOS settings. Actions are defined in the Dashboard and must be handled in the App code as well.
In the image below, you can check how the Category's name and custom actions have been set inside the Dashboard.
Image 1:
In the listed steps below, you will find how to define the needed Category and actions inside your iOS app code:
- Define the Category and Actions inside the App Delegate's didFinishLaunching method:
UNNotificationAction* enter = [UNNotificationAction
actionWithIdentifier:@"EnterAction"
title:@"Enter"
options:UNNotificationActionOptionForeground];
UNNotificationAction* snooze = [UNNotificationAction
actionWithIdentifier:@"SnoozeAction"
title:@"Snooze"
options:UNNotificationActionOptionForeground];
UNNotificationAction* dismiss = [UNNotificationAction
actionWithIdentifier:@"DismissAction"
title:@"Dismiss"
options:UNNotificationActionOptionDestructive];
UNNotificationCategory* category = [UNNotificationCategory
categoryWithIdentifier:@"CustomCategory"
actions:@[snooze, enter, dismiss]
intentIdentifiers:@[@""]
options:UNNotificationCategoryOptionCustomDismissAction];
UNUserNotificationCenter* center = [UNUserNotificationCenter currentNotificationCenter];
[center setNotificationCategories:[NSSet setWithObjects:category, nil]];
let dismiss = UNNotificationAction(identifier: "DismissAction", title: "Dismiss", options: .destructive)
let snooze = UNNotificationAction(identifier: "SnoozeAction", title: "Snooze", options: .foreground)
let enter = UNNotificationAction(identifier: "EnterAction", title: "Enter", options: .foreground)
let category = UNNotificationCategory(identifier: "CustomCategory", actions: [snooze, enter, dismiss], intentIdentifiers: [""], options: [.customDismissAction])
UNUserNotificationCenter.current().setNotificationCategories([category])
- Set the Notification Center Delegate. If you have already done that, continue to the next step.
[UNUserNotificationCenter currentNotificationCenter].delegate = self;
let userNotifCenter = UNUserNotificationCenter.current()
userNotifCenter.delegate = self
- Configure the actions code and handle the user's response:
-(void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)(void))completionHandler
{
NSDictionary *userInfo = [[[[response valueForKey:@"notification"] valueForKey:@"request"] valueForKey:@"content"] valueForKey:@"userInfo"];
if([[response actionIdentifier] isEqual:@"SnoozeAction"]||[[response actionIdentifier] isEqual:@"EnterAction"]||[[response actionIdentifier] isEqual:@"DismissAction"]){
int64_t i = [[userInfo objectForKey:@"_lpn"] longLongValue];
NSMutableDictionary * args = [[NSMutableDictionary alloc] init];
NSString* action = [[response actionIdentifier] stringByReplacingOccurrencesOfString:@"Action" withString:@""];
NSDictionary* actions = [userInfo objectForKey:@"LP_KEY_PUSH_CUSTOM_ACTIONS"];
if(actions != NULL){
[args setObject:[actions objectForKey:action] forKey:action];
}
LPActionContext *context = [LPActionContext actionContextWithName:LP_PUSH_NOTIFICATION_ACTION args:args messageId:[NSString stringWithFormat:@"%lld",i]];
[context runTrackedActionNamed:action];
}
}
@available(iOS 10.0, *)
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
let userInfo = response.notification.request.content.userInfo
// Check if any of the custom actions is executed
if response.actionIdentifier == "SnoozeAction" || response.actionIdentifier == "EnterAction"
|| response.actionIdentifier == "DismissAction" {
if let mid = userInfo["_lpn"] as? Int64 { // get the message id
// Use the below code to track only the message event without executing an action
// You can also use context?.runActionNamed(action)
//let ct = Leanplum.createActionContext(forMessageId: String(mid))
//let action = response.actionIdentifier.replacingOccurrences(of: "Action", with: "")
//ct?.trackMessageEvent(action, withValue: 0, andInfo: "", andParameters: nil)
var args = [AnyHashable:Any]()
let action = response.actionIdentifier.replacingOccurrences(of: "Action", with: "")
if let actions = userInfo[LP_KEY_PUSH_CUSTOM_ACTIONS] as? [AnyHashable:Any] { // access the custom actions _lpc
args[action] = actions[action]
}
let context = LPActionContext.init(name: LP_PUSH_NOTIFICATION_ACTION, args: args, messageId: String(mid)) // Init the context with the arguments, messageId and the reserved name - __Push Notification
context?.runTrackedActionNamed(action) // Execute the Action and track the message event
}
}
completionHandler()
}
Keep in mind that you need to set the Category to be used in the Push Notification in the Dashboard. You can check the image at the begging of the section - Image 1.
This is how the custom actions look:
The custom actions are triggered by long-pressing the push notification.
Note that if the custom action 'Opens' the app, the custom action could be overridden by the main Open Action of the Push Notification.
Badge counts
The app icon badge is meant to display a count of unread items that await the user's attention. By design, this relies entirely on a remote server to be the system of record; each push notification can set the count with a badge attribute in the payload. If the value is 0, iOS will clear the count and remove the badge. If a value is not set in the payload, the count remains the same.
For example, this payload sets the badge to 9.
{
"aps" : {
"alert" : "You got your emails.",
"badge" : 9
},
"acme1" : "bar",
"acme2" : 42
}
This payload clears the badge, even if the user does not open the notification. A silent push works the same way, but would not include an alert value.
{
"aps" : {
"alert" : "You got your emails.",
"badge": 0
},
"acme1" : "bar",
"acme2" : 42
}
In the Composer, you can set the badge value in your push notification to any number. Leaving the field blank will do nothing (i.e. maintain the current badge count), and setting it to zero will clear the count.
You cannot increment the existing badge count directly from Leanplum's dashboard, but you can add some code to your app to increment and clear the badge count.
To increment the badge whenever a push is received in the background, add the following to didReceiveRemoteNotification in your delegate:
func application(_ application: UIApplication,
didReceiveRemoteNotification userInfo: [AnyHashable : Any],
fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
// Only increment badge if in background.
if application.applicationState == .background {
UIApplication.shared.applicationIconBadgeNumber += 1
}
completionHandler(.newData)
}
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
// Only increment badge if in background.
if(application.applicationState == UIApplicationStateBackground) {
NSInteger badge = [[UIApplication sharedApplication] applicationIconBadgeNumber];
[[UIApplication sharedApplication] setApplicationIconBadgeNumber: badge + 1];
}
completionHandler(UIBackgroundFetchResultNewData);
}
To clear the count on app start or resume, you could implement the following in your applicationDidBecomeActive delegate method. However, this would clear the count even if they open the app by some means other than opening the push notification.
func applicationDidBecomeActive(_ application: UIApplication) {
// Clear app badge on start or resume.
UIApplication.shared.applicationIconBadgeNumber = 0
}func applicationDidBecomeActive(_ application: UIApplication) {
// Clear app badge on start or resume.
UIApplication.shared.applicationIconBadgeNumber = 0
}
func applicationDidBecomeActive(_ application: UIApplication) {
// Clear app badge on start or resume.
UIApplication.shared.applicationIconBadgeNumber = 0
}
Custom alert sounds
You can set a custom alert sound for a push notification via the Leanplum Message Composer. However, before doing so, you must include the sound file in your app, following Apple's guidelines for file type, length and size. See Preparing Custom Alert Sounds.
Once the file is available in your app, simply enter the filename with the extension for the iOS push option sound for the message.
Testing
Once you've set up push notifications, test that it is working properly. Send a push notification to your development devices. Run your app, wait a few seconds, and then press the home button.
Troubleshooting
- On the device, verify the app is configured to enable pushes: Settings -> Notification Center -> {Your app name}
- Recreate the provisioning profile. See iOS provisioning portal.
Android push notifications
Android push setup, push services, notification channels, and customization
See more on push notification services, Android Notification Channels, and customizing push notifications:
Adaptive icons warning (Resolved in Leanplum Android SDK 4.2.2+)
Using adaptive icons for push on Android Oreo without a fallback icon can cause problems that may require a factory reset of the device. Please add a regular icon with name leanplum_default_push_icon.png to your drawable folder. Google issue: https://issuetracker.google.com/issues/68716460
Using push notification services (Android)
Follow the setup instructions in the Android SDK setup for basic push functionality. Leanplum supports Firebase Cloud Messaging (FCM).
Generally, you can configure your Android project to use Leanplum push services with other custom or third-party push notification services by creating custom classes that extend the correct Leanplum push messaging classes.
Create a custom class to receive push messages
You may want to create a custom class (if you haven't already) to define the behavior for your project after receiving a Push Message. This can be helpful if you plan on using the information from the push payload or using the push customizer.
For Firebase, the new class .Custom_FirebaseMessagingService must extend the LeanplumPushFirebaseMessagingService class (which is extending FirebaseMessagingService).
public class Custom_FirebaseMessagingService extends LeanplumPushFirebaseMessagingService {
public void onMessageReceived(RemoteMessage remoteMessage) {
super.onMessageReceived(remoteMessage);
Log.i("### ", "Firebase message received");
}
}
Your custom class must include the onMessageReceived method, which should override its parent method but still call super.onMessageReceived(). This will make sure the Leanplum code in the extended class is executed when the push notification is received. You can place any custom code within this method after this call.
We will invoke this custom class later with a service in the Android Manifest.
Invoke the custom class in your Manifest
Assuming you have already implemented our SDK and modified your Application class and AndroidManifest.xml file as documented in the Android setup, you will just need to make a few modifications to your Manifest in order to use the push services.
For Firebase, you'll need to edit the service for the com.google.firebase.MESSAGING_EVENT intent so that it invokes the .Custom_FirebaseMessagingService class.
Change the android:name attributes to point to your custom classes.
<service
<!-- android:name="com.leanplum.LeanplumPushFirebaseMessagingService" -->
android:name=".Custom_FirebaseMessagingService"
android:exported="false">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
GitHub sample project for push services
A sample Firebase project is available here, and you can see its manifest file here.
Android Notification Channels
Introduced as part of Android 8.0 (Oreo), notification channels give developers more control over push notification customization and management.
If your audience includes Android 8 users, you must implement one or more notification channels in order to send notifications to your users. Sending a push notification to Android 8 users without specifying a valid notifications channel will cause the message to fail to post, and the system will log an error.
Notification channels allow app developers to categorize their push notifications based on the type of content. With channels, you can modify the color of the notification, the sound and vibration pattern it makes, how prominently the notification appears on the user's device, and more. See Android's documentation for more info.
1. Set up Android Notification Channels in Leanplum
Developers can add, delete, or modify notification channels via our API.
See our API docs for more on the methods addAndroidNotificationChannel and deleteAndroidNotificationChannel.
2. Send a push notification with Android Notification Channels
Once a developer adds a notification channel via our API, your new channel should be accessible for use in the Campaign Composer.
Select your desired channel from the dashboard to send a push notification. The Notification channel setting should be in Advanced options in the push notifiction's settings.
Make sure you test any new channels on a real device before sending notifications out to users.
3. See all notification channels defined in Leanplum
To see all the notification channels defined in Leanplum, see our API docs for more information on the getAndroidNotificationChannels API call.
These channels are also visible in the Leanplum Campaign Composer — just click the current Notification channel under Advanced Options to reveal a dropdown with all of your available Android notification channels.
Customizing push notifications 🎨
You can customize the appearance of notifications using LeanplumPushService.setCustomizer in your Application class's onCreate method. From there, you can perform any customizations you want to the NotificationCompat object. You can also use the customizer to easily be notified of incoming pushes from Leanplum. You can access some of the notification's properties, like the message, using bundle.getString("lp_message"), and any custom values you defined in Advanced Options > Data in the message config through the dashboard.
For a detailed example, see our guide on Customizing push notifications.
It's a good practice to show additional information or navigate to the correct part of your app when users open your push notifications. You can do that using Leanplum's in-app messaging, which integrates nicely with push notifications.
Unity push notifications
Unity push notifications setup
To setup push notification support, Leanplum provides a couple of built-in methods that will help you.
iOS setup for push
On iOS, use the following code to register your app for remote notifications after Leanplum starts. This simply registers through the iOS framework for alerts, badges, and sounds.
Leanplum.RegisterForIOSRemoteNotifications();
Alternatively, if you want to register in a custom way, you can add the registration code in your Objective-C code.
For iOS, you'll also need to upload your certificates to Leanplum. Refer to the iOS SDK docs for instructions.
Android setup for push
On Android, you can setup Firebase Cloud Messaging through Gradle.
Go to the project folder -> Assets -> Plugins -> Android. Open the mainTemplate.gradle file. Uncomment the Firebase dependencies.
classpath 'com.google.gms:google-services:3.0.0'
compile 'com.leanplum:leanplum-fcm:' + LP_VERSION
compile('com.google.firebase:firebase-messaging:[10.0.0,)')
apply plugin: 'com.google.gms.google-services'
Set the desired firebase-messaging version.
Add your google-services.json file in the Assets folder. You can also add it after the project is exported depending on your setup.
Add the Google Server API key in the Leanplum App settings on the Dashboard.
On Android, you'll also need to add some items to your manifest. Refer to the Android SDK setup for the appropriate code.
It's a good practice to show additional information or navigate to the correct part of your app when users open your push notifications. You can do that using Leanplum's in-app messaging, which integrates nicely with push notifications. There's no additional coding needed.
Test Push Notifications
Once you've set up push notifications, test that it is working properly by sending a push notification to your development devices.
JavaScript push notifications
Leanplum supports web push (HTML5) notifications (Contact your CSM to activate this feature).
Supported browsers
Web Push is an experimental technology supported in the following browsers:
Desktop
- Supported in Chrome 42+, Edge 17+, and Firefox 44+ (disabled in ESR versions)
- Not supported in Internet Explorer and Safari
Mobile
- Supported on Android in Chrome, Firefox, and Samsung Internet
- Not supported on iOS (due to platform limitations)
New methods
Four public methods have been added to the HTML5 SDK to support web push.
isWebPushSupported
Determines if web push is supported by the user's browser. Returns a boolean of True or False.
isWebPushSubscribed
Determines if the user is subscribed for web push. Returns a boolean of True or False.
registerForWebPush
Registers the user's browser for web push.
unregisterFromWebPush
Unsubscribes the user and removes the push token. (Does not unregister the service worker registration.)
These can be used to prompt the user to register for web push. We have provided a sample implementation where a toggle registers and unregisters a user for web push. Specifically, see implementation for toggle initialization and toggle switching.
Enabling Web Push
To enable web push, you first need to include the Service Worker file in your root directory and register it in your code, so the browser can use it to run the necessary background processes.
Our SDK has a method that handles the Service Worker registration for you; you just need to pass the filepath to registerForWebPush. The Leanplum SDK also includes a fully working sw.min.js file that you can use as your Worker.
For security reasons, service workers only run over HTTPS.
Before registering the device for push, we recommend checking that the device and browser support web push, and that the device has not already been registered. You can do this with isWebPushSupported and isWebPushSubscribed, both of which return a boolean.
import leanplum from `leanplum-sdk`
let isSubscribedToWebPush = false;
let isWebPushSupported = leanplum.isWebPushSupported();
if(isWebPushSupported){
leanplum.isWebPushSubscribed().then(subscriptionStatus => {
isSubscribedToWebPush = subscriptionStatus;
});
}
Then, you can register the device for push:
if(isWebPushSupported && !isSubscribedToWebPush){
// Register by passing SW filepath (which is in our root directory).
leanplum.registerForWebPush('/sw.min.js').then(subscriptionStatus => {
console.log('Subscription status: %s', subscriptionStatus);
});
}
After registering successfully, the device will be able to receive web push notifications, and the browser will handle displaying the messages themselves. The notification includes the title, message, icon, and push image (beta). However, Firefox and Opera do not support push images on any platform.
You can customize how your notification is displayed via the showNotification function.
- Title is required. If the push notification does not include a title, the
showNotificationfunction will drop the push on the SDK side. - We only support Open URL as the action. The SDK will ignore other actions.
For more information on sending a web push from Leanplum, see how to Send a web push notification.
ReactNative push notifications
To enable Push Notifications on iOS, you need to upload your certificates to Leanplum and register for remote notifications in your app.
Swizzling
We use swizzling to collect push tokens. If your app has other SDKs installed that also use method swizzling it may cause conflicts. See here for manual instructions.
iOS push setup
- Login to the iOS provisioning portal.
- In the Identifiers > App IDs, select your app, click Edit, and enable Push Notifications.
- Click Create Certificate for each of the Development and Production certificates and follow the onscreen instructions. You should not reuse existing certificates so that we can track delivery failures properly.
- Download your new certificate files from your browser. Open the files on your computer, which will launch Keychain.
- In Keychain, select the new certificates, expand them to view the private key, and then right click to export them as .p12 files. You must enter a password.
- In Leanplum, go to your app's Keys & Settings (App Settings > {Your app} > Keys & Settings). Under Push Notifications, upload your .p12 files to Leanplum and enter your passphrase from step 5 above.
- Configure your app to use push notifications in your app delegate's
applicationDidFinishLaunchingmethod.
AppDelegate.m
// Required to register for notifications
- (void)application:(UIApplication *)application didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings
{
[RNCPushNotificationIOS didRegisterUserNotificationSettings:notificationSettings];
}
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
{
[RNCPushNotificationIOS didReceiveRemoteNotification:userInfo fetchCompletionHandler:completionHandler];
// Needs to be called if swizzling is disabled in Info.plist otherwise it won’t affect SDK if swizzling is enabled.
[Leanplum didReceiveRemoteNotification:userInfo
fetchCompletionHandler:completionHandler];
}
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
{
// Needs to be called if swizzling is disabled in Info.plist otherwise it won’t affect SDK if swizzling is enabled.
[RNCPushNotificationIOS didRegisterForRemoteNotificationsWithDeviceToken:deviceToken];
[Leanplum didRegisterForRemoteNotificationsWithDeviceToken:deviceToken];
}
- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error
{
[RNCPushNotificationIOS didFailToRegisterForRemoteNotificationsWithError:error];
// Needs to be called if swizzling is disabled in Info.plist otherwise it won’t affect SDK if swizzling is enabled.
[Leanplum didFailToRegisterForRemoteNotificationsWithError:error];
}
// Required for the localNotification event.
- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification
{
[RNCPushNotificationIOS didReceiveLocalNotification:notification];
}
Then you have to install @react-native-community/push-notification-ios
Install
yarn add @react-native-community/push-notification-ios
For React-Native with 0.60 or newer (react-native >= 0.60) only need to do this
cd ios && pod install
For React-Native project under 0.60 (react-native <= 0.59)
react-native link @react-native-community/push-notification-ios
And the last thing to do is to require a permission for the notifications after response from Leanplum SDK.
import {Alert,Platform} from 'react-native';
import PushNotificationIOS from "@react-native-community/push-notification-ios";
Leanplum.onStartResponse((success: boolean) => {
const alertTitle = success
? 'Leanplum session started'
: 'Leanplum session not started';
Alert.alert(alertTitle);
Platform.OS === 'ios' ? PushNotificationIOS.requestPermissions() : null;
});
Android setup
To enable Push Notifications on Android you need to add Leanplum FCM as well as Firebase library to your project.
Modify generated build.gradle located in android project directory. Also, make sure that you have applied the com.google.gms.google-services plugin in your application's build.gradle.
dependencies {
...
implementation 'com.leanplum:leanplum-fcm:5.3.3'
implementation 'com.google.firebase:firebase-messaging:20.1.4'
...
}
apply plugin: 'com.google.gms.google-services'
If the following error is thrown,Plugin with id 'com.google.gms.google-services' not found, check whether the classpath 'com.google.gms:google-services:4.3.3' is set properly in the build.gradle's dependencies. For example:
...
dependencies {
classpath("com.android.tools.build:gradle:3.5.3")
classpath 'com.google.gms:google-services:4.3.3'
}
...
The final step is to add google-services.json file in the <react_native_project>/android/app folder.
App inbox
Instructions for setting up a platform-agnostic messaging channel
App Inbox is a standalone messaging channel that doesn't require push certificates and can store messages for long periods of time.
There is no default UI for the inbox, so you can code your inbox to fit your app's branding and goals. Many Leanplum users make their inbox messages lead to a full-screen message when tapped, with a Back button that leads users back to the inbox.
App Inbox messaging requires the following SDK version:
- iOS SDK 1.6.0
- Android SDK 2.1.0
- React Native SDK 1.0.0
- JS SDK 1.6.0
To get an instance to the App Inbox object, use the following code:
Leanplum.inbox()
[Leanplum inbox]
Leanplum.getInbox();
var inbox = Leanplum.inbox()
LeanplumInbox.inbox().then(inbox=>{
//...
}).catch(error=>{
//...
})
Getting data from Leanplum
Messages and Inbox data are retrieved from Leanplum on Leanplum.start(). The start method returns data asynchronously, so you should use the provided callback (onChanged for iOS and React Native, and InboxChangedCallback for Android), which will be called automatically when updates to the inbox are finished loading from Leanplum.
Here is a simple example of how you can use the callback to update the number of unread messages:
Leanplum.inbox().onInboxChanged {
unreadCountLabel.text = String(Leanplum.inbox().unreadCount)
}
[[Leanplum inbox] onChanged:^() {
unreadCountLabel.text = [NSString stringWithFormat:@"%@", @([[Leanplum inbox] unreadCount])];
}];
Leanplum.getInbox().addChangedHandler(new InboxChangedCallback() {
public void inboxChanged() {
unreadCount.setText(Integer.toString(Leanplum.getInbox().unreadCount()));
}
});
Leanplum.inbox().onChanged(function() {
var unreadCount = String(Leanplum.inbox().unreadCount());
document.getElementById('unreadCountLabel').innerText = unreadCount;
});
LeanplumInbox.inbox().onChanged(CALLBACK_FUNCTION)
Because messages are only retrieved on start, a user will not receive a new message until they begin a new session. You can, however, use forceContentUpdate to re-sync with Leanplum mid-session, which will fetch any new App Inbox messages, and automatically invoke callbacks you have registered.
forceContentUpdate will also update all of your variables, user attributes, messages and A/B tests, so depending on how your app is set up to work with Leanplum, it can affect the functionality and UI of your app. For more on forceContentUpdate, including some precautions, see Syncing with Leanplum mid-session.
Inbox message counts
After data is finished loading from Leanplum, you can get the total and unread count of messages using the LPInbox (iOS) or LeanplumInbox (Android) object:
let inbox: LPInbox = Leanplum.inbox()
let count: UInt = inbox.count
let unreadCount: UInt = inbox.unreadCount
LPInbox *inbox = [Leanplum inbox]; // get the shared Inbox object
NSUInteger count = [inbox count];
NSUInteger unreadCount = [inbox unreadCount];
LeanplumInbox inbox = Leanplum.getInbox(); // instantiate new Inbox object
int unread = inbox.unreadCount();
int total = inbox.count();
var inbox = Leanplum.inbox()
var count = inbox.count()
var unreadCount = inbox.unreadCount()
let inbox: LPInbox = Leanplum.inbox()
let count: UInt = inbox.count()
let unreadCount: UInt = inbox.unreadCount()
Inbox messages
After data is finished loading from Leanplum, you can use the following LPInbox (iOS) or LeanplumInbox (Swift, Android and React Native) methods to get a list of messages.
// Use the LeanplumInbox instance to get messages (LeanplumInbox.Message). You'll get an NSArray of message objects - either all or just the user's unread messages.
let allMessages: [LeanplumInbox.Message] = inbox.allMessages
let unreadMessages: [LeanplumInbox.Message] = inbox.unreadMessages
// Use the LPInbox instance inbox to get messages. You'll get an NSArray of message objects - either all or just the user's unread messages.
NSArray *allMessages = [inbox allMessages];
NSArray *unreadMessages = [inbox unreadMessages];
//This method currently returns a list of NewsfeedMessage objects, which you'll need to cast to the new InboxMessage objects.
List all = inbox.allMessages();
List<LeanplumInboxMessage> messages = (List<LeanplumInboxMessage>) all;
// Do stuff with messages.
var allMessages = inbox.allMessages();
var unreadMessages = inbox.unreadMessages();
const message = await LeanplumInbox.message(MESSAGE_ID);
const title = message.title;
const subtitle = message.subtitle;
const timestamp = message.timestamp;
if (message.isRead) {
// Do something if the message has been read.
}
Alternatively, you could use messagesIds to get an NSArray or list of all the message IDs, then loop through the returned items and call messageForId with each messageId to fetch each message:
// Use the LeanplumInbox instance to get message Ids.
let messageIds = inbox.messagesIds
for messageId in messageIds {
let message: LeanplumInbox.Message = inbox.message(id: messageId)
// Do stuff with message.
}
// Use the LPInbox instance inbox to get message Ids.
NSArray *messageIds = [inbox messagesIds];
for(int i = 0; i < [messageIds count]; i++){
LPInboxMessage *message = [inbox messageForId: messageIds[i]];
// Do stuff with message.
}
LeanplumInbox inbox = Leanplum.getInbox(); // instantiate new Inbox object
List<String> messagesIds = inbox.messagesIds(); // get all message IDs
for (String messageId : messagesIds) {
LeanplumInboxMessage message = inbox.messageForId(messageId);
// Do stuff with the message.
}
var messageIds = inbox.messagesIds()
messageIds.forEach(function(messageId) {
var message = inbox.message(messageId)
// Do stuff with message.
})
Message attributes
Once you have the Message object, you can access its title, subtitle, and deliveryTimestamp for display.
let message: LeanplumInbox.Message = Leanplum.inbox().message(id: messageId)
let title: String = message.title
let subtitle: String = message.subtitle
let timestamp: Date? = message.deliveryTimestamp
LPInboxMessage *message = [[Leanplum inbox] messageForId: messageId];
NSString *title = [message title];
NSString *subtitle = [message subtitle];
NSDate *timestamp = [message deliveryTimestamp];
LeanplumInboxMessage message = inbox.messageForId(messageId);
String title = message.getTitle();
String subtitle = message.getSubtitle();
Date timestamp = message.getDeliveryTimestamp();
var message = Leanplum.inbox().message(messageId)
var title = message.title()
var subtitle = message.subtitle()
var timestamp = message.timestamp()
You can also easily test if an inbox message is read:
if (message.isRead) {
// Do something if the message has been read.
}
if ([message isRead]) {
// Do something if the message has been read.
}
if (message.isRead()) {
// Do something if the message has been read.
}
if (message.isRead()) {
// Do something if the message has been read.
}
Message image
Inbox messages can be customized with an image.
If you haven't disabled automatic image downloads (image prefetching), you can get the image path on the device:
let image = UIImage(contentsOfFile: message.imageFilePath ?? "")
UIImage *image = [UIImage imageWithContentsOfFile:[message imageFilePath]];
String imageFilePath = message.getImageFilePath();
Bitmap bitmap = BitmapFactory.decodeFile(imageFilePath);
var message = Leanplum.inbox().message(MESSAGE_ID);
var url = message.imageUrl();
const message = await LeanplumInbox.message(MESSAGE_ID);
const imageFilePath = message.imageFilePath;
const url = message.imageURL;
If you want to disable automatic image downloads on sync (image prefetching), you can do so with the LPInbox method disableImagePrefetching before calling start.
Leanplum.inbox().disableImagePrefetching()
Leanplum.start()
[[Leanplum inbox] disableImagePrefetching];
[Leanplum start];
LeanplumInbox.disableImagePrefetching();
Leanplum.start();
If you do so, you'll need to get the image URL for each message. For iOS, use LPInboxMessage method imageURL (imageURL value for Swift). For Android, use LeanplumInboxMessage method getImageUrl. Then download the files yourself later, or use a third-party solution to manage this for you.
If prefetch is enabled or the image has already been downloaded, imageURL(iOS) or getImageUrl(Android) will return the URI to the file on the device.
// Get the Image URL for a single LPInboxMessage.
let url: URL = message.imageURL
// Use the URL to download the image.
// Get the Image URL for a single LPInboxMessage.
NSURL *url = [message imageURL];
// Use the URL to download the image.
var url = message.imageUrl()
Handling user interactions
After a user reads the message, you need to explicitly set the inbox message as read. You need to make sure the message is marked as read every time the user interacts with it, since this also triggers the Open Action for that message. You'll also need to remove the message if the user deletes it.
//Mark message as read.
message.read()
//Delete inbox message.
message.remove()
//Mark message as read.
[message read];
//Delete inbox message.
[message remove];
//Mark message as read.
message.read();
//Delete inbox message.
message.remove();
var inbox = Leanplum.inbox();
// Mark message as read and trigger Open Action
inbox.read(MESSAGE_ID);
// Mark message as read without triggering Open Action
inbox.markAsRead(MESSAGE_ID);
// Delete inbox message
LeanplumInbox.remove(MESSAGE_ID)
const message = await LeanplumInbox.message(MESSAGE_ID);
//Mark message as read.
LeanplumInbox.read(MESSAGE_ID)
//Delete inbox message.
LeanplumInbox.remove(MESSAGE_ID)
Implementing Inbox in your app (iOS only)
A natural UIView for App Inbox is UITableView because table cells have title, subtitle and image properties.
You can see our example implementation of App Inbox in a TableView in Objective C and in Swift.
Be sure to use onChanged to handle asynchronous changes from Leanplum.
Updating from newsfeed
In previous versions of our SDK (iOS SDK >= 1.3.8 < 1.6.0, Android SDK >= 1.2.17 < 2.1.0), App Inbox was called Newsfeed. We renamed the classes (and added a bit more functionality to the new classes) in iOS SDK 1.6.0 and Android 2.1.0.
Since the new classes have the same methods as the previous Newsfeed and NewsfeedMessage classes, actions like getting messages, getting message counts, marking messages as read, removing messages, etc. can be done as before using the same methods on the new classes.
iOS classes
Newsfeed is now LPInbox and NewsfeedMessage is now LPInboxMessage.
To update your code, you simply need to instantiate the Inbox with the new class and update your object types.
Android classes
Newsfeed is now LeanplumInbox and NewsfeedMessage is now LeanplumInboxMessage.
To update your code, you simply need to instantiate the Inbox with the new class and update your object types. For handling updates, you also need to switch to the new addChangedHandler method with an InboxChangedCallback for callbacks (these replace addNewsfeedChangedHandler and NewsfeedChangedCallback).
Geofencing and location-based messaging
Using location tracking with Leanplum
You can set up geofence and iBeacon regions as criteria to trigger in-app messages and push notifications, right from the dashboard.
To enable this functionality in your app, follow the setup instructions below:
iOS location services
Setting up iBeacons and location services
Android location services
Setting up location services
React Native location services
Setting up location services
How it works
By default, Leanplum uses IP-based geolocation for all users. The Leanplum SDK also captures GPS/cell-based geolocation from the user's device when available.
GPS/cell-based information is always trusted more — when available, we use the latitude/longitude coordinates from the device and use reverse-geocoding to determine the name of the Country, Region, and City.
Geolocation is gathered only on app start or resume, so a user's location is based on their most recent session.
We provide methods to disable automatic collection of GPS/cell-based geolocation (not IP), as well as a method to set user location manually, which allows you to truncate the lat/long to a custom number of decimal points, ultimately limiting the precision of geolocation and protecting user privacy. You could, for example, truncate to two decimal places, blurring the accuracy to about 1 kilometer. See our iOS and Android location docs for more on these methods.
If you set up a message or campaign with a geofence region that has a radius smaller than 20km, we only include users with CELL or GPS Location accuracy. (Because this kind of campaign is very location-specific, we require a high degree of confidence to run it.) If the geofence radius is 20km or larger, we will also include users with IP Location accuracy.
iOS location services
Setting up geolocation regions and location-based messaging in Leanplum
Set up location services in Leanplum
To set up location services for iOS, you'll have to add our location framework to your Podfile, or as an additional linked framework:
pod 'Leanplum-iOS-Location'
pod 'Leanplum-iOS-LocationAndBeacons'
If you need iBeacons as well, include our Location and Beacons framework instead.
If you did a manual install, add CoreLocation to Build Phases -> Link Binary With Libraries.
Geolocation is only available on iOS SDK 1.4+.
Get permission from users
On iOS, your app must get permission from a user to access their location while using your app. Our SDK automatically asks a user for permission on your behalf. You simply need to import the Location library into your AppDelegate, and add NSLocationWhenInUseUsageDescription to your info.plist file.
import LeanplumLocation
You can disable the automatic iOS prompt by setting the authorizeAutomatically property of LPLocationManager to false before calling start.
LPLocationManager.shared().authorizeAutomatically = false;
Leanplum.start()
[LPLocationManager sharedManager].authorizeAutomatically = NO;
[Leanplum start];
If you've disabled automatic authorization, you can request permission using the authorize method. Optionally, you can check if the user has already given permission with needsAuthorization.
if(LPLocationManager.shared().needsAuthorization) {
LPLocationManager.shared().authorize()
}
LPLocationManager * LPLocation = [[LPLocationManager alloc] init];
if(LPLocation.needsAuthorization){
[LPLocation authorize];
}
iOS will only allow you to request access to a user's location once. After that, the user must navigate to the privacy settings for your app in Settings.
Disabling location collection
If you do not want to send GPS/Cell location to Leanplum, then do not include either of the location frameworks above. Alternatively, you can include the frameworks and call disableLocationCollection before start.
Leanplum.disableLocationCollection()
[Leanplum disableLocationCollection];
Manually set user location (iOS SDK 1.4.3+)
Our SDK now allows you to set user location by calling setDeviceLocationWithLatitude after start. You should call disableLocationCollection before setting the user location.
Leanplum.setDeviceLocationWithLatitude(40.748817, longitude: -73.985428)
Set up location-based messaging
To trigger messages and push notifications based on geofence and iBeacon regions, you'll have to follow the setup instructions above, then add a few keys in your info.plist file:
- Add the key
NSLocationWhenInUseUsageDescription: This text will appear to the user when prompting for permissions to enable location monitoring in the foreground on iOS 8+. (Required for in-app messages) - Add the key
NSLocationAlwaysUsageDescription: This text will appear to the user when prompting for permissions to enable location monitoring in the background on iOS 8+. (Required for push notifications) - Add the key
NSLocationUsageDescription: This text will appear to the user when prompting for permissions to enable location monitoring in the foreground or background on iOS 7. (Optional for in-app messaging and push notifications) - Add the key
Required background modesif it does not already exist. Within it, add a background mode App registers for location updates. (Required for push notifications)
iOS limits the number of locations that an app can save to 20, however, our SDK manages this for you by only storing the nearest 20 locations to each user's device at any given time. This way, you can have more than 20 locations saved in Leanplum, but your users will only have the 20 most relevant to them (i.e. the closest).
Region monitoring is available in iOS 7 and higher.
Android location services
Setting up geolocation regions and location-based messaging in Leanplum
In Android, our SDK (v1.3+) automatically tracks GPS/cell-based location if available to your app.
Disable location collection (Android)
If you do not want to send GPS/Cell location to Leanplum, you can call disableLocationCollection beforestart.
Leanplum.disableLocationCollection();
Manually set user location (Android SDK 2.0.0)
Our SDK allows you to set user location by calling setDeviceLocation with two arguments (see below) after calling start. You should call disableLocationCollection before setting the location.
location
android.location.Location
The location object representing the user's current location.
type
LeanplumLocationAccuracyType
The type of geolocation. Either CELL (default) or GPS (more precise).
setDeviceLocation(location, type);
Set up location-based messaging (Android)
To use geofence regions to trigger messages and push notifications in your app, you must add a few keys in your AndroidManifest.xml file:
- Add the permission
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>(Required for geofencing) - Link the Google Play services library to your project and add the metadata in the application tag.
<meta-data android:name="com.google.android.gms.version" android:value="@integer/google_play_services_version" />(Required for geofencing)
Due to a limitation with Android, you can only have 100 regions active for your app at any given time. Leanplum optimizes usage by not monitoring regions for messaging campaigns that don't target particular users.
React Native location services
Setting up geolocation regions and location-based messaging in Leanplum.
For React Native, depending on device type where we want to install the SDK, we either need to follow the instructions from iOS or Android section
Disabling location collection
If you do not want to send GPS/Cell location to Leanplum, then do not include either of the location frameworks above. Alternatively, you can include the frameworks and call disableLocationCollection before start.
Leanplum.disableLocationCollection()
Manually set user location
Our SDK allows you to set user location by calling setDeviceLocationWithLatitude after start. You should call disableLocationCollection before setting the user location.
Leanplum.setDeviceLocationWithLatitude(LATITUDE, LONGITUDE, ACCURACY_TYPE )
// ACCURACY_TYPE is enumerator with possible values: IP = 0, CELL = 1, GPS = 2
How to integrate external Attribution services (App setup)
Integration
Leanplum integrates with many third-party attribution providers. The providers send Leanplum a postback that contains all of the publisher information, which in turn allows you to view the marketing channels in Leanplum, as well as target by them for A/B tests or marketing campaigns.
Before you turn the integration on with your specific partner, you need to confirm that your Leanplum integration is using the same device ID as the third-party you're hoping to integrate with. This is important because Leanplum matches the user install with the third-party postback using the device ID, so if they do not match Leanplum cannot report the data.
For example, Adjust sends IDFV device IDs for iOS, so in order for Leanplum to work with Adjust, you'll have to set your Leanplum device ID to IDFV as well. See Device IDs for more detail.
Once the device ID is confirmed, you can contact your third-party provider or log into their portal, and select Leanplum as the partner to send the postback to. You should then provide the App ID and Client Key specific to your Leanplum dashboard. These keys can be found in the Keys & Settings next to the app name in question.
To reach Keys & Settings, go to App Settings either by clicking on your name in the upper right-hand corner, or by clicking Manage Apps in the app selection bar at the top of the dashboard.
Device IDs in Leanplum
In Leanplum, the device IDs are captured automatically, but can vary depending on the version of the OS the device is running. Our SDK sets the device ID to the ANDROID_ID on newer versions (Android 6+), and an MD5 hash of the MAC address on older versions. The same is true for iOS: new versions (iOS 6+) use identifierForVendor while older versions use a hash of the MAC address.
You can, however, choose to set the device ID mode or the device ID yourself. See Device IDs for more detail.
API Introduction
Leanplum is designed to integrate with your app using our SDKs. If you need to supplement our SDKs with information from your backend systems, you can use our API. The Leanplum API is only available to customers on full-service accounts. We recommend you contact your customer success manager to discuss your API integration needs, so we can help review your plan and share some best practices.
Check out the sample uses of our API on Github.
Tracking analytics data
Leanplum organizes its data within sessions. A session is one use of an application by a user. To start a session, use the start API method. Sessions may be paused and resumed as users exit and re-enter the app, using pauseSession and resumeSession.
Sessions also include user activity, such as events (things the user does), and states (parts of the app the user is in). Send events and states using track and advance. User attributes may change within a session using setUserAttributes. Sessions time out after 2 hours of inactivity, or 30 minutes after being paused. To keep a session alive, use heartbeat.
Some things can happen outside of sessions as well. For example, a user may receive a notification or a friend request while they're not using the app. You can use track (with disposition=passive) and setUserAttributes outside of sessions.
Updating user information
Leanplum stores many things in a user's profile, including user attributes and behavioral data, such as the lifetime occurrences of a particular event. You can update both of these with setUserAttributes. To retrieve a user's profile, use exportUser.
Sending messages
Leanplum allows you to create messages that are triggered based on user activity, such as when an event fires or a user attribute changes. But in some cases, you may need to trigger messages based on information available on your own server. Use the sendMessage API to send a push notification, newsfeed message, or other types of messages to a user.
The Leanplum SDK will automatically collect push tokens from your users, but if you need to import older push tokens (from users before Leanplum), you can set them in setDeviceAttributes.
Accessing content
There are APIs for receiving the user's variables and files set in the Leanplum dashboard in the Variables and A/B Testing tabs. Whenever a request executes the start method, the list of variables is returned. You can also get the user's variables without starting a new session using getVars. To access the file referenced by the value of a file variable, use downloadFile.
Attribution
Use setTrafficSourceInfo to send attribution data for a user. You can also use setUserAttributes.
Data export
You can export raw user activity data using exportData, export lists of users that match certain criteria using exportUsers, or export analytics reports using exportReport. You can also add postbacks to stream data to another location when those events occur using addPostback.
Getting info about content in Leanplum
You may need to retrieve information about A/B tests or messages created in the Leanplum dashboard. To retrieve information about A/B tests, use getAbTests, getAbTest , or getVariant. To retrieve information about messages, use getMessage or getMessages.
Making requests
All API requests must be made to https://api.leanplum.com/api. The API is very flexible and supports GET and POST request formats. Our docs, generally, use POST examples for methods that might update/change Leanplum data (e.g. start or setUserAttributes), but both GET and POST request methods are acceptable.
Request formats
GET
The top-level arguments are sent as GET parameters in the request. As part of the HTTP specification, a URL must be at most 2,083 characters, so it's best for small API requests. All characters in the URL should be URL encoded.
Example:
https://api.leanplum.com/api?appId=APP_ID&clientKey=CLIENT_KEY&apiVersion=1.0.6&userId=USER_ID&action=setUserAttributes&userAttributes={"Interests":["Technology","Music"]}
Properly encoded this is:
https://api.leanplum.com/api?appId=APP_ID&clientKey=CLIENT_KEY&apiVersion=1.0.6&userId=USER_ID&action=setUserAttributes&userAttributes=%7B%27Interests%27%3A+%5B%27Technology%27%2C+%27Music%27%5D%7D
POST
The top-level arguments can be either in the URL or in the request body. Generally, our docs show the action argument set in the URL for POST requests, but this is not necessary; all arguments can be set in the POST body with the request sent to https://api.leanplum.com/api. Alternatively, more (even all) arguments can be placed in the URL. However, all characters in the URL should be URL encoded.
The Content-Type of the POST request must be JSON or multipart form data.
Example of a POST request with JSON:
POST https://api.leanplum.com/api?action=setUserAttributes
Content-Type: application/json
{
"appId": "APP_ID",
"clientKey": "CLIENT_KEY",
"apiVersion": "1.0.6",
"userId": "USER_ID",
"userAttributes": {
"Interests": [
"Technology",
"Music"
]
}
}
Multiple actions can be batched together. It's highly recommended to batch API actions, especially for the same user. Do not make multiple concurrent API requests for the same user. See Batching requests for more.
Authentication
Client keys
Every Leanplum app you create will have its own set of API keys (you can find them in App Settings > Keys & Settings). You'll use the clientKey to authenticate and make API requests. Each key has different permissions, and the correct key for each API method is listed in the method's description.
Production
track sessions, states, set user or device attributes, etc.
Development
import CSV data, delete users, set variables, etc.
Data Export
export user data, messaging and a/b test reports, etc.
Content Read-only
get message and a/b test information, etc.
Use secure key handling practices 🔐
You should take all possible measures to keep your Leanplum API keys safe and secure — always send your Keys and any other secure data in the request body when making post requests. Follow these recommendations for more security best practices.
Selecting a user
For many API methods, you can pass a userId, a deviceId, or both userId and deviceId. Here's how these will affect the action, or execution of the API method:
- userId only: The API action pertains to the user and not a particular device. In most cases, Leanplum will not associate any device with this action, which may cause device information to be excluded from analytics. In some cases, Leanplum may be able to infer the device. When using the
sendMessagemethod for a push notification, passing auserIdonly would send a push notification to all devices associated with that user. - deviceId only: The API action pertains to a particular device without an associated user ID, for example, a logged out user. The user ID may be inferred by the API.
- userId and deviceId: The API action pertains to a particular device associated to a particular user ID.
In many cases, you would only pass a userId and not a deviceId, because events coming from the API are not associated with a particular device. Events coming from a device are usually sent via the SDK.
// Example POST requests to
// https://api.leanplum.com/api?action=sendMessage
// User only - sends message to all devices for that user
{
"appId": "APP_ID",
"clientKey": "PROD_KEY",
"apiVersion": "1.0.6",
"userId": "USER_ID",
"messageId": 101001
}
// Device only - sends to a specific device
{
"appId": "APP_ID",
"clientKey": "PROD_KEY",
"apiVersion": "1.0.6",
"deviceId": "DEVICE_ID",
"messageId": 101001
}
// User and device - sends to a specific device and user
{
"appId": "APP_ID",
"clientKey": "PROD_KEY",
"apiVersion": "1.0.6",
"userId": "USER_ID",
"deviceId": "DEVICE_ID",
"messageId": 101001
}
Additionally, getVars has an additional mode where neither a userId nor deviceId are provided, which will return the default variables that would be seen by a blank user.
Responses
Success, warning, and error responses for the API
All responses except for the downloadFile request are in JSON format, and include warning and error objects to call out any problems with your request. The 200 status with success flag only indicates that the request was received, not that the provided API action was successful.
An HTTP 200 response with success=true does not indicate that an action was successful — you must verify there are no error or warning messages. A successful call will always result in a 200 status with success=true and no error.message or warning.message.
Types of responses:
Success or warning: HTTP 200
success=true (no warning or error objects): The request was received and the action taken successfully.
{
"response": [
{
"success": true
}
]
}
success=true and warning.message=“warning message": The request was received successfully but the action was likely skipped. See warning message for details.
{
"response": [
{
"success": true,
"warning": {
"message": "User not found; request skipped."
}
}
]
}
success=true and error.message=“error message": The request was received successfully but the action was skipped. See error message for details.
{
"response": [
{
"success": true,
"error": {
"message": "Error message provided here."
}
}
]
}
Errors: HTTP 4xx, 5xx
success=false and error.message=“error message": The request was not received successfully. See error messages below for details on each error and how to resolve.
Invalid access key - Provide the correct key to resolve.
{
"response": [
{
"success": false,
"error": {
"message": "Invalid access key"
}
}
]
}
429 strict device locking or rate limit
Strict device locking - Caused by multiple different API calls to the same user profile at the same time. It makes sure no GET or POST requests can be executed at the same time, thus ensuring consistent data.
DevMode Rate limit - For requests where devMode=true, there is a maximum rate limit of 1 request per second. Error message: "Per device rate limit exceeded." or "Too Many Requests."
Retry with exponential backoff to resolve, up to a maximum amount of retries. For example, you could use exponential backoff starting with 5 seconds up to a minute.
{
"response": [
{
"success": false,
"error": {
"message":"Per device rate limit exceeded for 1 devices."
}
}
]
}
451 Unavailable For Legal Reasons.
This status code is returned when the API call is attempting to update a userId which has been blocked with the block api call:
https://docs.leanplum.com/v1/reference#post_api-action-block
5xx error - Retry with exponential backoff to resolve.
Retrying
You should retry if you receive an HTTP 5xx (internal server) error or a 429. Use exponential backoff, up to a maximum amount of retries. For example, you could:
- retry after 2 +/- random(0, 1) seconds, then
- retry after 4 +/- random(0, 2) seconds, then
- retry after 8 +/- random(0, 4) seconds
- etc.
Billing
Make sure to review your contract for the exact details of your agreement.
Due to our system architecture, our costs increase for every user profile we need to retrieve in order to process an HTTP request to our API. As such, we bill server-side API calls based on the number of user lookups per HTTP request to our API, and not the number of HTTP requests or API actions. To be clear, for billing purposes, a server-side API call is one unique user lookup initiated by an HTTP request made to the Leanplum API from any source other than the Leanplum SDK.
One HTTP request can include multiple user lookups. multi allows you to batch multiple actions affecting up to 50 unique users in a single request. Therefore, the maximum number of billable API calls (user lookups) per HTTP request is 50, not 1.
We do, however, exempt all deleteUser actions from billing so you're free to clean up your data.
We also optimize how we handle multi requests to prevent duplicate user lookups (reducing both our and your costs). Because we do this, you can reduce the number of billable calls you make to the Leanplum API (while executing the same number of actions) by batching actions for the same user(s).
See Batching requests for more.
Multi
The multi action also allows you to import an external file containing a potentially large number of actions. This is billed by breaking the file into API requests that each contains a batch of 50 actions. Each of these API requests is then billed using the above definition.
Session definition
For billing purposes, a “User Session” is defined as a call of the API Method start or resumeSession that is made 30 (thirty) minutes or more after the latest call of the API Method pauseSession on the same device.
Batching requests with multi
Use a multi call to batch multiple API requests
To improve performance and reduce your billable API calls, you should batch multiple API requests that affect the same users as much as possible (see more on Reducing billable requests).
While Leanplum processes a successful multi call, concurrent requests for these users (via the SDK or API) will be queued and completed after the multi batch completes.
Send the request
To batch API actions, make a post request to https://api.leanplum.com/api?action=multi with the following parameters in the request body:
appId
required
The application ID. To find yours, select your app in the navigation column, and click Edit Apps. Under Keys, click Show.
clientKey
required
The Production key for your Leanplum App.
apiVersion
required
The version of the Leanplum API to use. The current version is 1.0.6.
time
required
The time at which the request was issued (UNIX time). This is used to resolve the time difference between Leanplum and the API caller when computing the times of events.
data
required
A JSON-encoded list of API methods to execute. All methods must be for the same app referred to by the appId parameter. Each data object must include the required arguments for its API action.
Each nested object in the data array is an individual API action, and must include the required parameters for that method. You'll need to reference each nested API method's documentation for info on required parameters and options.
{
"appId": "YOUR_APP_ID",
"clientKey": "YOUR_PROD_KEY",
"apiVersion": "1.0.6",
"time": 1375295890,
"data": [
{
"action": "start",
"time": 1375295825,
"userAttributes": {
"Gender": "Male",
"Age": 25
},
"userId": "user1"
},
{
"action": "track",
"time": 1375295830,
"event": "Level Complete",
"params": {
"Level": 1,
"Score": 100
},
"userId": "user1"
}
]
}
Response
Each individual action will also have its own response; some may be successes, some may have warnings, and some may have errors. The index of the response will match the index of the request in the data array. For the example above, if we pass a bad string as the action in the second batched request, we get the following response:
{
"response": [
{
"success": true
},
{
"success": false,
"error": {
"message": "Action not found"
}
}
]
}
The 'multi' call method is limited to 50 users and/or 500 actions in a single call. All calls in a batch with more than this will be ignored and the server will return a 403 status. Each unique user lookup in a request is a billable API call. See billing and costs for more.
Reduce billable requests
Batching should first be done on a per-user basis, and then across users, with up to 50 users modified in a single multi request. For example, if you want to track an event and setUserAttributes for two users, the total billable calls is entirely dependent on how you setup your multi requests.
DO
Batch by user first. For example:
multi request 1:
- track (user1)
- setUserAttributes (user1)
multi request 2:
- track (user2)
- setUserAttributes (user2)
Each of these requests requires one user lookup, so the total billable API calls is two (despite executing four actions). In this case, however, you could combine these into a single multi request without using additional API calls:
multi request 1:
- track (user1)
- setUserAttributes (user1)
- track (user2)
- setUserAttributes (user2)
This single request still only requires two unique user lookups, so the total billable API calls remains two.
AVOID
Batching requests by action instead of by user.
multi request 1:
- track (user1)
- track (user2)
multi request 2:
- setUserAttributes (user1)
- setUserAttributes(user2)
In this case, each request requires two user lookups. Therefore, there are four billable API calls. Avoid this, as it is not an efficient use of multi.
Debugging
To debug an API request, set clientKey to your development key and devMode=true, and most importantly be sure to set the userId and/or deviceId to a test user/device. If you use a real user when debugging, Leanplum will convert that user profile to a developer account.
These requests will be visible in the Dashboard Debugger console.
All session and tracking info from these requests will not be tracked as real user data. The matching user/device will be set to a developer account, and all of their activity will be sent to a separate analytics section, Developer Activity.
Do not set devMode=true for any API request that involves a real device or user. Only use it with test devices or developer accounts.
API methods
See all of our API methods organized by section below. Make sure to use the best practices outlined under Making requests when working with these methods. You can also download our postman collection here:
Not sure where to start?
See our API introduction for more on using Leanplum's API, or see our API guides for more specific applications.
User Behavior
Use these API methods when tracking user behavior like event and session data.
For billing purposes, a “User Session” is defined as a call of the API Method start or resumeSession that is made 30 (thirty) minutes or more after the latest call of the API Method pauseSession on the same device.
advance
Advances a user to the next state. If the user/device does not exist, the API request is skipped and a warning will be returned. You can modify this behavior with the createDisposition option (see below). The state is the section of the app the user is currently in. States are like events with duration.
This method requires your production API clientKey.
pauseState
Pauses the current state, but not the session, for a user. If the user/device does not exist, the API request is skipped and a warning will be returned. You can modify this behavior with the createDisposition option (see below).
This method requires your production API clientKey.
resumeState
Resumes the current state for a user. If the user/device does not exist, the API request is skipped and a warning will be returned. You can modify this behavior with the createDisposition option (see below).
This method requires your production API clientKey.
pauseSession
Pauses the current session and state for a user. If the user/device does not exist, the API request is skipped and a warning will be returned. You can modify this behavior with the createDisposition option (see below).
This method requires your production API clientKey.
resumeSession
Resumes the current session and state for a user. Use either after pauseSession, or start if the app started in the background. If the user/device does not exist, the API request is skipped and a warning will be returned. You can modify this behavior with the createDisposition option (see below).
This method requires your production API clientKey.
heartbeat
Sends a pulse to indicate that the current session is still in progress, so as not to automatically end it. Sessions are automatically timed out after 2 hours of inactivity — or 30 minutes if the session was paused first. If the user/device does not exist, the API request is skipped and a warning will be returned. You can modify this behavior with the createDisposition option (see below).
This method requires your production API clientKey.
start
Starts a new session and returns the variables that have changed for the user. If the user/device does not exist, a new user will be created (see the createDisposition option below). This method requires your production API clientKey.
stop
Ends the current session. If the user/device does not exist, the API request is skipped and a warning will be returned. You can modify this behavior with the createDisposition option (see below). This method requires your production API clientKey.
track
Tracks one occurrence of an event for a user. If the user/device does not exist, a new user will be created (see the createDisposition option below). This method requires your production API clientKey. See tracking events via API for examples.
User Information
Use these API methods to update user and device information. You can also block and delete users via the Leanplum API.
For billing purposes, a “User Session” is defined as a call of the API Method start or resumeSession that is made 30 (thirty) minutes or more after the latest call of the API Method pauseSession on the same device.
setUserAttributes
Sets user attributes for the user given by userId and/or deviceId. If the user has an open session, the attributes for the current session will also be updated. Attributes will then propagate on data going forward. User properties not supplied in this method will not be affected. If the user/device does not exist, a new user will be created (see the createDisposition option below).
This method requires your production API clientKey.
setDeviceAttributes
Sets device-level attributes, such as a deviceId or an iOS push token, for the current device. If the device is shared between multiple users, pass a userId with the deviceId to update the device for each user. (Passing just the deviceId will only update it once). If the device already exists, the attributes will be updated. If the device and user do not exist, a new user will be created along with this device (see the createDisposition option below). See selecting a user for more.
See Importing iOS push tokens for an example using setDeviceAttributes. This method requires your production API clientKey.
At least one of the following must be set to create/update a device:
appVersion,systemName,systemVersion,browserName,browserVersion,deviceName,deviceModel,iosPushToken,gcmRegistrationId, orwebPushSubscription.
setTrafficSourceInfo
Sets traffic source information for the current session of a user. If the user/device does not exist, a new user will be created (see the createDisposition option below). This method requires your production API clientKey.
registerDevice
Registers the current device for development. This method requires your development API clientKey.
deleteUser
Permanently removes all of a user’s attribute information from our database. To erase a user’s data entirely — including attribute, analytics, and sessions data — set the fullErasure flag to true. You can bulk delete users with multi (import mode) or contact our support team for assistance. deleteUser calls are exempt from API billing.
This method requires your development API clientKey.
block
Stops all data collection for a user going forward. block also erases any data previously associated with that user, including all of their attribute and analytics data. The block will take effect as soon as the “success” response is returned. It may take up to 15 days to delete the user’s data completely.
Note that the block call deletes a user's past data, but does not delete the user entirely. This allows you to use the unblock call to resume data collection.
unblock
Resumes data collection for a specific user, and ends a previous block on data collection. This will not restore any of data that was deleted by the previous block call.
Messages
Use the following API methods to get messages, unsubscribe categories, Android notification channels, and send messages.
getMessage
Gets information about a message. This method requires your content read-only API clientKey.
getMessages
Gets information about all the messages for a given app. This method requires your content read-only API clientKey.
getUnsubscribeCategories
Gets information about all the email subscription categories for a given app. This method requires your content read-only API clientKey.
sendMessage
Sends a message (typically a push notification) to one device or user. You must provide a deviceId and/or a userId. If deviceId is provided, the message will be sent to the corresponding device only; if only userId is provided, the message will be sent to all devices of the user with specified userId. If the user/device does not exist, the API request is skipped and a warning will be returned. You can modify this behavior with the createDisposition option (see below).
Messages are queued, so they will be sent after the request completes.
This method requires your production API clientKey.
Example for sending values in the sendMessage API
See below for an example sendMessage API call with two values.
https://api.leanplum.com/api?appId=YOUR_APPID&clientKey=YOUR_CLIENT_KEY&apiVersion=1.0.6&action=sendMessage&userId=YOUR_USER_ID&messageId=ENTER_MESSAGE_ID&values={"key":"val", "key2":"val2"}
Once you pass the values via the API, you'll be able to access them in the body of the message in the Leanplum dashboard. For example, if you set values={"name":"Donna", "points":5} in the API call, you could format your message body in the dashboard to say, "Hey Donna! congrats on getting 5 points today!"
To insert the values into your message body, use the Insert value tool {{ or format your message content as follows: "Hey {{Value "name"}}! Congrats on getting {{"points"}} today!"
Jinja formatting for values
Note that any of the following will work in the above case:
{{"points"}}{{Value "points"}}{{value['points']}}(optimal for use in HTML code to avoid double quotes conflicts)
addAndroidNotificationChannel
Creates new notification channels and updates existing ones. Updateable parameters include channel name, description, and default. This method requires your development API clientKey.
Leanplum will not interfere with any channel defined directly in your app code or by other mobile marketing providers. For instance, if your app has both a “Promotions” channel and a “Transactional” channel, but your marketer should only have access to Promotions, then you should only send that channel to Leanplum.
getAndroidNotificationChannels
Gets all Android notification channels defined in Leanplum and their associated settings. No additional arguments are required. This method requires your production API clientKey.
This method requires your production API clientKey.
deleteAndroidNotificationChannel
Permanently removes an Android notification channel from Leanplum. This method requires your development API clientKey.
Note — You cannot delete the channel that is defined as default (unless it is the only channel). To delete the default channel, mark another channel as default first.
getAbTest
Gets information about an A/B test. This method requires your content read-only API clientKey.
getAbTests
Gets information about A/B tests. This method requires your content read-only API clientKey.
getVariant
Gets information about an A/B test variant. This method requires your content read-only API clientKey.
getVars
Gets the variable values for the current user or device. If the user/device does not exist, the API request is skipped and a warning will be returned. You can modify this behavior with the createDisposition option (see below). To see default variable values, use the includeDefaults parameter.
This method requires your production API clientKey.
setVars
Sets the list of variables to be used in the Leanplum content management system. This method requires your development API clientKey.
deleteVars
Deletes the list of variables from the Leanplum content management system. This method requires your development API clientKey.
downloadFile
Redirects to a file uploaded to the Leanplum dashboard (the File tab). Must be the only API method in a batch. This method requires your production API clientKey.
uploadFile
Uploads up to 16 files or 50 MB at a time to use in the File picker and File tab of the Leanlum dashboard. This method requires your development API clientKey.
Export Data
Use these API methods to work with postbacks, data exports, reports, and user info.
addPostback
Adds a postback rule to be triggered by certain Leanplum message or A/B test events. The maximum number of postbacks allowed is three per app. This method requires your data export API clientKey.
See type and channels below for all possible events that can trigger postbacks. To see what values can be returned by the resulting POST request, see postbackUrl with an example template below.
Postback retry policy: If your app is unresponsive, Leanplum's request will timeout in 30 seconds. If the error code is 5xx, we will retry up to 9 more times with exponential backoff, starting from 1 hour, up to 10 hours. If all 10 times fail, the data is lost.
Example URL template for postbackUrl
Note that the values in the curly braces must be exactly as shown below, but you can customize the keys however you like in your template. For example, message_id={{Message ID}} or messageId={{Message ID}} will both work just fine.
https://api.service.com/v1/example?message_id={{Message ID}}&event={{Message event}}&device_id={{Device ID}}&user_id={{User ID}}&time={{Trigger time}}&channel={{Message channel}}&template_name={{Template name}}¶meters={{Parameters}}
Make sure your URL is encoded before posting
Un-encoded URLs can cause the request to fail or add the postback incorrectly. Postman and certain browsers often encode the URL for you, which is why our example contains & instead of %26.
listPostbacks
List current postback rules. This method requires your data export API clientKey.
deletePostback
Deletes a particular postback. This method requires your data export API clientKey.
exportData
Exports raw data to downloadable files. Data is split into roughly 256 MB files, and is not necessarily ordered. Exports can be made in JSON or CSV format. For JSON format, each file contains 1 line per session, with each session JSON-encoded. For CSV format, data is split into separate files for sessions, states, events, event parameters, and user attributes.
Note that for daily exports, you may see some variation in sessions data results. This is because sessions data can come in up to 8 days after a user's last interaction with your app.
Export files are automatically deleted 24 hours after export occurs. Data becomes available to export every 2 hours, and only for complete sessions. You cannot export data that has become available more than 60 days ago. You may only export data 24 times per day. Exports with invalid arguments do not count towards this limit.
Use getExportResults with the returned jobId to get the results.
Note
If you configure the exportData API to send the files to an S3 bucket, they are not being stored on our servers afterwards. In this case, they cannot be accessed via the link in getExportResults.
exportData schema (csv, S3 bucket)
Our exportData API method, if called with exportFormat = csv or part of an S3 export, produces several csv files with the following schema.
CSV
The CSV export produces separate files for sessions, states, events, event parameters, and user attributes.
Sessions
sessionId
BIGINT
unique session ID for the current session
userId
VARCHAR(400)
user ID
userBucket
INT
LP-assigned user bucket
userStart
TIMESTAMP
start time of the user's first session
country
VARCHAR(256)
user's current country
region
VARCHAR(256)
user's current region
city
VARCHAR(256)
user's current city
start
TIMESTAMP
start time of session information
duration
TIMESTAMP
the duration of the current session in seconds (e.g. 19.254)
lat
REAL
latitude of current location
lon
REAL
longitude of current location
locale
VARCHAR(256)
user's locale, includes language and country set on the device (e.g. en_US)
timezone
VARCHAR(256)
user's timezone
timezoneOffset
DECIMAL(7,2)
offset of user's timezone
appVersion
VARCHAR(128)
current app version
client
VARCHAR(50)
current client version
sdkVersion
VARCHAR(128)
SDK version
osName
VARCHAR(512)
OS name
osVersion
VARCHAR(512)
OS version
deviceModel
VARCHAR(512)
model of user's device
browserName
VARCHAR(512)
user's web browser
browserVersion
VARCHAR(512)
version of user's web browser
deviceId
VARCHAR(512)
device ID
priorEvents
BIGINT
number of prior events
priorSessions
BIGINT
number of prior sessions
priorTimeSpentInApp
DECIMAL(12,2)
total time of previous session(s)
priorStates
BIGINT
number of prior states
isDeveloper
BOOLEAN
is developer flag
isSession
BOOLEAN
is session flag
sourcePublisherId
VARCHAR(200)
attribution source publisher ID
sourcePublisher
VARCHAR(200)
attribution source publisher
sourceSubPublisher
VARCHAR(200)
attribution source sub-publisher
sourceSite
VARCHAR(200)
attribution source site
sourceCampaign
VARCHAR(200)
attribution source campaign
sourceAdGroup
VARCHAR(200)
attribution source ad group
sourceAd
VARCHAR(200)
attribution source ad
sessionId
BIGINT
the ID of the matching session
name
VARCHAR(256)
the name of the user attribute
value
VARCHAR(512)
the value of the user attribute
experimentId
BIGINT
unique experiment ID for the A/B test
sessionId
BIGINT
the ID of the matching session
variantId
BIGINT
the variant ID the user is entered in within the A/B test
stateId
BIGINT
unique state ID
sessionId
BIGINT
the ID of the matching session
stateName
VARCHAR(256)
the name of the state
start
TIMESTAMP
the start time within the state
duration
DECIMAL(10,2)
time within state
info
VARCHAR(1024)
any info attached to the state
timeUntilFirstForUser
DECIMAL(12,2)
total time before state occurrence, would be null value if this is not the first occurrence
eventId
BIGINT
unique event ID
stateId
BIGINT
the ID of the matching state, if any
sessionId
BIGINT
the ID of the matching session
eventName
VARCHAR(256)
the name of the event
start
TIMESTAMP
the start time of the event
value
FLOAT
value of the event
info
VARCHAR(1024)
any info attached to the event
timeUntilFirstForUser
DECIMAL(12,2)
total time before event occurrence, would be null value if this is not the first occurrence
eventId
VARCHAR
the ID of the matching event
name
VARCHAR(256)
the name of the event parameter
value
VARCHAR(512)
the value of the parameter
exportReport
Exports statistics for an A/B test, message, bookmarked report, or user activity in your app (as in the Analytics tab of the dashboard) over specified period of time. Report data becomes available to export every 2 hours, and only for complete sessions. You may only export report data 100 times per day per app. Exports with invalid arguments do not count towards the limit.
This method requires your data export API clientKey.
Use getExportResults with the returned jobId to get the results.
exportData schema (JSON)
Our exportData API method exports a file where each row is a single JSON object representing a single user session.
Schema
Each row in the export will have a JSON object with the following attributes. Attributes marked as optional below may not be returned (if the value is null).
isSession
boolean
whether this data is counted towards a session
appVersion
string
optional
the version of the app used on this device (e.g. 2.0.1)
country
string
optional
the country the user is in, specified by ISO 2-letter code (e.g. US for United States)
timezone
string
optional
the timezone abbreviation for the user. See list of timezone abbreviations
region
string
optional
the region (state) the user is in (e.g. ca for California)
city
string
optional
user's current city
locale
string
optional
user's locale, includes language and country set on the device (e.g. "en_US")
deviceModel
string
optional
the model name of the device (e.g. iPad)
priorEvents
int64
the number of prior events
systemName
string
optional
the name of the OS the current device is running (e.g. iOS)
systemVersion
string
optional
the version number of the OS the current device is running (e.g. 6.0)
priorStates
int64
number of prior states
time
double
the time the session started in seconds (UTC)
deviceId
string
optional
the device ID for the user's device
firstRun
double
the time of the user's first session
sourcePublisherId
string
optional
ID of the publisher used to refer the user (e.g. 1001)
sourcePublisher
string
optional
name of the publisher used to refer the user (e.g. Big Fish Games)
sourceSubPublisher
string
optional
name of the developer used to refer the user (e.g. GameDeveloper1)
sourceSite
string
optional
name of the app or website used to refer the user (e.g. MyLittleApp)
sourceCampaign
string
optional
name of the campaign used to refer the user (e.g. US CPI)
sourceAdGroup
string
optional
name of the ad group used to refer the user (e.g. banners)
sourceAd
string
optional
name of the ad used to refer the user (e.g. blue1)
userId
string
the user ID defined by your app. See more on user IDs in iOS and Android.
client
string
optional
the type of client (e.g. js)
browserName
string
optional
the name of the browser the current device is running (e.g. Chrome)
browserVersion
string
optional
the version number of the browser the current device is running (e.g. 17.0)
sdkVersion
string
the SDK version running on the user's device
sessionId
string
the unique session ID for the current session
lat
double
optional
latitude of user's current location
lon
double
optional
the longitude of the user's current location
duration
double
optional
the duration of the current session in seconds (e.g. 19.254)
priorTimeSpentInApp
double
the total time of previous session(s) in seconds
timezoneOffsetSeconds
int32
optional
the offset of user's timezone in seconds
priorSessions
int64
the number of prior sessions
userBucket
int32
the user bucket assigned to this user (0-999)
isDeveloper
boolean
optional
whether the user is a developer and not a user
experiments
array
see experiments
states
array
see states
userAttributes
object
an object with key/value pairs based on the attributes you define.
id
int64
the ID of the A/B test or message the user is enrolled in
variantId
int64
the ID of the variant the user is placed within the test
impressed
boolean
optional
whether the user has seen the test after being assigned to it.
stateId
integer
the ID for the state
info
string
optional
any info attached to the state
time
double
optional
the time in seconds (UTC) the user reached the state
duration
double
optional
the duration of the state in seconds (UTC)
name
string
optional
the name of the state
timeUntilFirstForUser
double
optional
the time the user spent in the app (in seconds) before reaching the state for the first time. If this is not the first occurrence, this will be null.
parameters
object
key/value pairs for any parameters you passed (in your code) with the state
events
array
an array of events triggered while user was in this state
events[].eventId
int64
the ID for the event
events[].value
double
the value passed to the event (within your code)
events[].info
string
optional
any info attached to the event.
events[].time
double
the time in seconds (UTC) the event occurred
events[].name
string
the name of the event (defined in your code)
events[].timeUntiFirstForUser
double
optional
the time the user spent in the app (in seconds) before triggering the event for the first time. If this is not the first occurrence, this will be null.
events[].parameters
object
key/value pairs for any parameters you passed (in your code) with the event
Example
The following is a single line from an export.
{
"country": "US",
"firstRun": 1499847141.448,
"priorStates": 0,
"city": "toledo",
"experiments": [
{
"id": 4848982474358784,
"variantId": 5318562523119616
}
],
"lon": "83.5552",
"locale": "en_US",
"isSession": false,
"deviceId": "aUsersDeviceId",
"states": [
{
"stateId": -8299958977733658706,
"events": [
{
"eventId": 6467624121712405504,
"timeUntilFirstForUser": 7191.943,
"name": "addToCart",
"time": 1506574496.416,
"value": 0.0,
"parameters": {
"productId": "10248"
}
]
}
],
"duration": 0.0,
"systemName": "Windows",
"browserVersion": "61",
"client": "js",
"browserName": "Chrome",
"lat": "41.66",
"priorSessions": 166,
"userAttributes": {},
"priorEvents": 4652,
"sessionId": "5116700603679946752",
"userId": "[email protected]",
"timezoneOffsetSeconds": 0,
"priorTimeSpentInApp": 316959.844,
"deviceModel": "Web Browser",
"sdkVersion": "1.1.7",
"time": 1506575007.376,
"region": "oh",
"userBucket": 133
}
Note: An actual export will have multiple lines of these JSON-formatted objects, separated by a new line (not a comma). See the example below, but note that I added the ellipses to represent attributes removed. These would never appear in an actual export.
{"country":"SG","firstRun":1.488267623088E9, ... "userBucket":725}
{"country":"ID","firstRun":1.499847141448E9, ... "userBucket":540}
{"country":"US","firstRun":1.984714144849E9, ... "userBucket":430}
exportUser
Retrieves attributes for the current user. This method requires your data export API clientKey.
exportUsers
Exports multiple user IDs. The export may be executed up to 40 times successfully per day, and this limit resets at 12:00 am PST each calendar day. This method requires your data export API clientKey.
Use getExportResults with the returned jobId to get the results.
Example exportUsers request: Exporting a saved audience
To export one of your audiences of users, use the audienceName param option. The full call would look something like:
https://api.leanplum.com/api?action=exportUsers&appId=YOUR_APP_ID&clientKey=YOUR_DATA_EXPORT_KEY&apiVersion=1.0.6&audienceName=My test audience
Once your request is successful, you'll get a jobId. Use this ID with the getExportResults call to get your final user list as a CSV.
getExportResults
Retrieves the result of an export job generated by: exportData, exportReport, and exportUsers. This method requires your data export API clientKey.
Import Data
Use these API methods to import historical data and other info using a multi csv upload.
multi (CSV)
Imports a (potentially large) CSV file with API actions to be executed. The file will be imported asynchronously as a job. Use getMultiResults to get the job status. This method requires your development API clientKey. See CSV uploads for more on using multi to upload user attributes, events, and device attributes.
The file should contain a header row with the API argument names that are found in the API documentation, such as userId, deviceId, and action (if defaultAction is not specified). To indicate nested JSON arguments, use a dot within the column name, such as userAttributes.Gender.
userId,userAttributes.Gender,userAttributes.Age
user1,Male,25
user2,Female,37
The file is broken into separate API requests that contain batches of 50 actions. Each unique user lookup in a batch is a billable API call. See billing and costs for more.
Whitespace in File
Leading and trailing whitespace in the userId column will not be trimmed. This can cause unintended new user profiles to be created if you are not careful.
multi (JSON)
Batches separate API actions. Each nested object in the data array is an individual API action, and must include the required parameters for that method. You'll need to check each nested API method's documentation for info on required parameters and options.
To improve performance and reduce your billable API calls, you should batch multiple API requests that affect the same users as much as possible (see more on Reducing billable requests).
POST https://api.leanplum.com/api?action=multi
The multi call method is limited to 50 users and/or 500 actions in a single call. All calls in a batch with more than this will be ignored and the server will return a 403 status. Each unique user lookup in a request is a billable API call. See billing and costs for more.
appId
required
The application ID. To find yours, select your app in the navigation column, and click Edit Apps. Under Keys, click Show.
clientKey
required
The Production key for your Leanplum App.
apiVersion
required
The version of the Leanplum API to use. The current version is 1.0.6.
time
required
The time at which the request was issued (UNIX time). This is used to resolve the time difference between Leanplum and the API caller when computing the times of events.
data
required
A JSON-encoded list of API methods to execute. All methods must be for the same app referred to by the appId parameter. Each data object must include the required arguments for its API action.
{
"appId": "YOUR_APP_ID",
"clientKey": "YOUR_PROD_KEY",
"apiVersion": "1.0.6",
"time": 1375295890,
"data": [
{
"action": "start",
"time": 1375295825,
"userAttributes": {
"Gender": "Male",
"Age": 25
},
"userId": "user1"
},
{
"action": "track",
"time": 1375295830,
"event": "Level Complete",
"params": {
"Level": 1,
"Score": 100
},
"userId": "user1"
}
]
}
import requests
import json
#Setup the body
rawJSON = dict()
rawJSON['appId'] = '<appKey>'
rawJSON['clientKey'] = '<devKey>'
#Base URL
url = "https://www.leanplum.com/api?apiVersion=1.0.6&action=multi"
#Array for each batch of 50
dataValues = []
counter = 1
#Setting batch size to 1 for testing
while(counter < 2):
#Each API call in the batch needs to be in a dictionary
node = dict()
node['action'] = 'track'
node['event'] = '<event>'
node['userId'] = '<userId>'
node['value'] = <numeric Value>
node['currencyCode'] = 'USD'
node['time'] = 1544173994
#Exclude this for production but, you can check debugger to see the API call came through.
node['devMode'] = True
#All API calls are aggregated in an array
dataValues.append(node)
#Increment counter
counter += 1
#Add our API array into the 'data' field
rawJSON['data'] = dataValues
print("JSON Body:")
print(json.dumps(rawJSON))
#Send out the POST request. It's important to use the data parameter not json
resp = requests.post(url,data=json.dumps(rawJSON))
print(resp)
import requests
import json
#Setup the body
rawJSON = dict()
rawJSON['appId'] = '<appKey>'
rawJSON['clientKey'] = '<devKey>'
#Base URL
url = "https://www.leanplum.com/api?apiVersion=1.0.6&action=multi"
#Array for each batch of 50
dataValues = []
counter = 1
#Setting batch size to 1 for testing
while(counter < 2):
#The attributes you want to update need to be stored in a dictionary
#See the setUserAttributes API for possible options
attrDict = dict()
attrDict['<attributeName>'] = '<attribute Value>
#Each API call in the batch needs to be in a dictionary
node = dict()
node['action'] = 'setUserAttributes'
node['userAttributes'] = attrDict
node['userId'] = '<userId>'
#Exclude this for production but, you can check debugger to see the API call came through.
node['devMode'] = True
#All API calls are aggregated in an array
dataValues.append(node)
#Increment counter
counter += 1
#Add our API array into the 'data' field
rawJSON['data'] = dataValues
print("JSON Body:")
print(json.dumps(rawJSON))
#Send out the POST request. It's important to use the data parameter not json
resp = requests.post(url,data=json.dumps(rawJSON))
print(resp)
While Leanplum is processing a successful multi call, concurrent requests for these users (via the SDK or API) will be queued and completed after the multi batch completes.
Response
Each individual action will also have its own response; some may be successes, some may have warnings, and some may have errors. The index of the response will match the index of the request in the data array. For the example above, if we pass a bad string as the action in the second batched request, we get the following response:
{
"response": [
{
"success": true
},
{
"success": false,
"error": {
"message": "Action not found"
}
}
]
}
getMultiResults
Gets the status of a multi import job. This method requires your development API clientKey.
Example:http://api.leanplum.com/api?action=getMultiResults&appId=APP_ID&clientKey=DEVELOPMENT_KEY&jobId=JOB_ID
API Limits
Make the most of Leanplum's API
Our API currently limits what you can call and track.
General limitations
- You may use up to 200 distinct user attribute names per app.
- You may use up to 500 event names per app.
- You may use up to 500 state names per app.
- You may use up to 200 distinct parameter names across all events and states per app.
- You may have up to 1500 events per session (SDK limit only).
- You may have up to 1500 states per session (SDK limit only).
- By default, apps cannot use the API to send us historical data. Contact us if you need to do so.
- The total size of your variables must be < 1 MB. (There is no fixed maximum number of characters for a String.)
devMode rate limit
Development mode gives your team members real-time information and additional logging for internal testing purposes. To ensure low latency, API calls with the devMode=true flag will be rate limited to 1 request per second (QPS) per user. The limit of 1 QPS is to avoid conflicting queries for the same user at the same time. When this limit is exceeded, the API will return an HTTP 429 error and SDK will return a 408.
Naming events, states, user attributes, and parameters
Rules apply to names and values:
- Length must be greater than 0 and fewer than 140 characters.
- Must not start with a period (.) or hyphen (-).
- Must not end with a forward slash (/).
- Must not contain commas (,), vertical pipes (|), double quotes ("), curly braces ({), tabs, newlines, or return characters.
- Must not be numeric.
- Must not use the same name for different events and states, e.g. you should not name a state
cartand an eventcart.
Naming rules for variables and message arguments
- Dots within names (.) indicate grouping. Objective-C macros use _ instead of .
- Dots, square brackets, parentheses, and backslashes [].()\ that should appear in the name must be escaped with a leading backslash. Dots used for grouping should not be escaped.
- User IDs and Device IDs must be at most 400 characters.
Method-specific limits
multi: Batches multiple API actions within a single request to the API. Each unique user lookup in a request is a billable API call.
Limited to 50 users and/or 500 actions in a single call. All calls in a batch with more than this will be ignored and the server will return a 403 status. Each unique user lookup in a request is a billable API call.
production
multi (import): Imports a CSV file with API actions to be executed.
Each unique user lookup in a batch is a billable API call.
development
downloadFile: Redirects to a file from Content Management.
Must be the only API method in a batch.
production
heartbeat: Indicates that the current session is still in progress, so not to automatically end it.
Sessions automatically time out after two hours of inactivity in the open app, or 30 minutes of inactivity if the session was paused first.
production
setDeviceAttributes: Sets attributes for the current device.
At least one of the following must be set to create/update a device: appVersion, systemName, systemVersion, browserName, browserVersion, deviceName, deviceModel, iosPushToken, gcmRegistrationId, or webPushSubscription.
production
deleteUser: Permanently removes a user from our database. This does not remove the user's activity from analytics.
If you want to bulk delete users, use multi (import mode) or contact your CSM. This method requires your development API clientKey.
development
uploadFile: Uploads up to 16 files or 50 MB at a time to use in the Leanplum content management system.
Uploads up to 16 files or 50 MB at a time to use in Leanplum’s filepicker. (Uploads up to 16 files or 50 MB at a time to use in the Leanplum content management system.)
development
deleteAndroidNotificationChannel: Permanently removes an Android notification channel from Leanplum.
Cannot delete default channel. To delete the default, mark another channel as default first.
development
exportData: Exports raw data to downloadable files. Data is split into roughly 256 MB files, and is not necessarily ordered. Exports can be made in JSON or CSV format.
Data becomes available to export every 2 hours, and only for complete sessions. You cannot export data that has become available more than 60 days ago. You may only export data 24 times per day. Exports with invalid arguments do not count towards this limit.
data export
exportReport: Exports statistics for an A/B test, message, bookmarked report, or user activity in your app (as in the Analytics tab of the dashboard) over a specified period of time.
Report data becomes available to export every 2 hours, and only for complete sessions. You may only export report data 100 times per day per app. Exports with invalid arguments do not count towards the limit.
data export
exportUsers: Exports multiple user IDs.
This may be executed up to 10 times successfully per day.
data export
addPostback: Adds a postback rule to be triggered on incoming data.
The maximum number of postbacks allowed is three.
data export
Creating new users with our API
Our API now includes an optional argument for production methods — createDisposition — which allows you to control whether or not Leanplum automatically creates a new user profile for a non-existing user (based on the user ID you pass in the call).
The createDisposition argument accepts two possible strings:
CreateIfNeededcreates a user with the given IDs if one does not already existCreateNeverrequires that the user already exists; otherwise the API call is skipped and a warning will be returned.
This was a substantial change to many of our API methods. Now you can set whether or not the following calls will create new user IDs for any users that don't exist yet in your app.
If your API calls rely on Leanplum creating new users, you may need to update them to use createDisposition=CreateIfNeeded (see the table of methods below).
Each API method has its own default setting for the createDisposition option.
Methods that will not create new users by default
advance
CreateNever
getVars
CreateNever
heartbeat
CreateNever
pauseSession
CreateNever
pauseState
CreateNever
resumeSession
CreateNever
resumeState
CreateNever
sendMessage
CreateNever
stop
CreateNever
Methods that will create new users by default
You can change this behavior by adding createDisposition=CreateNever to the call.
setDeviceAttributes
CreateIfNeeded
setTrafficSourceInfo
CreateIfNeeded
setUserAttributes
CreateIfNeeded
start
CreateIfNeeded
track
CreateIfNeeded
Sending messages manually via the API
You can send send messages manually to your users individually using your own server and our API. This method works for push notifications, app inbox messages, email, and webhooks.
Create the message
First, you need to create and start the message in the Leanplum Dashboard. Go to the Messaging dashboard and create a new message.
Select Push Notification, Email, Webhook, or App Inbox Message. Set the Delivery to "Manual." When you're finished composing the message and setting your targets, click "Start."
Create the sendMessage API call
You can trigger the message using the sendMessage method of our API. If you want to send the message to the selected user even if they do not match the targets, use force = true.
You must provide a deviceID and/or a userID. If deviceID is provided, the message will be sent to the corresponding device only; if you only provide the userID, the message will be sent to all devices of the user with specified userId. Messages are queued, so they will be sent after the call completes.
If the user/device does not exist, the API call is skipped and a warning will be returned. You can modify this behavior with the createDisposition option (see below).
apiVersion
required
string
The version of the Leanplum API to use. The current version is 1.0.6.
action
required
string
The API method. Set this to sendMessage.
appId
required
string
Your App ID. To find your appId, go to App Settings, find your app, and click Keys & Settings.
clientKey
required
string
Your Production API key. To find your clientKey, go to App Settings, find your app, and click Keys & Settings.
userId
required (unless you choose to use deviceId)
string
The user ID of the targeted user. You can set this to an email address or whatever you use at your company for user IDs. Leave it blank to use the device ID. For more info, see Selecting a user.
messageId
required
number
The ID of the message, found in the URL when viewing a message (e.g. www.leanplum.com/dashboard#/{APP_ID}/messaging/{MESSAGE_ID}.
createDisposition
string
The policy that determines whether users are created by the API. Possible values:
CreateIfNeededcreates a user with the given IDs if one does not already exist.CreateNeverrequires that the user already exists, otherwise the API call is skipped and a warning will be returned.
The default value for this method is CreateNever
deviceId
only required if you do not include a userId
string
A unique ID for the device targeted by the call. You must provide a deviceId and/or a userId. See Selecting a user.
devMode
boolean
Whether the user is in Development Mode, i.e. the caller is a developer and not a user. This is important for reporting purposes. Default: false.
force
boolean
Whether to send the message regardless of whether the user meets the targeting criteria. Default: false.
values
object
A JSON object of key-value pairs to override Jinja values in the message. For example, if the message text is Hello {{Value "name"}}, you have {{Value "numMessages"}} new messages!, you can set values to {"name": "Bob", "numMessages": 5} to produce: Hello Bob, you have 5 new messages!
Important note: If certain values are set to null, Leanplum will return the key as a string. See more details to prevent this issue.
Setting attribution source data via the API
When working with a 3rd party attribution provider to pass leanplum install source information, the partner should send the following call:
https://api.leanplum.com/api?action=setTrafficSourceInfo&appId={APP_ID}&clientKey={CLIENT_KEY}&deviceId={DEVICE_ID}&client=HasOffers&sdkVersion=1.0.6&trafficSource={"publisherId":"","publisherName":"","publisherSubPublisher":"","publisherSubSite":"","publisherSubCampaign”:”}
This will pass the all install publisher information to Leanplum and allow you to view all analytics as well as target by publisher source.
Exporting bookmarked reports
Using the exportReport API call, we can export bookmarked reports from the dashboard.
Step 1 Create the bookmarked metric
For our example, we are going to look at the metric "Weekly Active Users". We will apply the filter "OS name is iOS and City is San Francisco". We can then book mark this metric and name it as "LP Weekly SF and iOS"
Step 2 Export the metric with exportReport
https://api.leanplum.com/api?appId=<app_id in app settings>&clientKey=<export key in app settings>&apiVersion=1.0.6&startDate=20160601&endDate=20160607&action=exportReport&dataType=BookmarkedReport&bookmarkName=LP Weekly SF and iOS
The above API call will create a job ID in the response. Make sure to fill in the start and end date that you would like.
{
response: [
{
jobId: "report_3936002_0400cd02-1754-468d-8beb-6456345142",
success: true
}
]
}
Step 3 Retrieve the results with getExportResults
https://api.leanplum.com/api?appId=<app_id in app settings>&clientKey=<export key in app settings>&apiVersion=1.0.6&action=getExportResults&jobId=report_3936002_0400cd02-1754-468d-8beb-6456345142
Step 4 Interpreting the Results
The response will contain all of the bookmarked metrics. Here are some sample ones. The JSON output is partitioned by date.
In this case, our base metric was Weekly Active Users, and it will be Total weekly users here.
Basic Stats:
{
Total first-time users: 20,
Total session length: 33,
Retained 14th day: 4,
Total users from 3rd day: 19,
Total weekly users: 2023,
Retained 3rd day: 2,
Retained 7th day: 8,
Total users from 30th day: 22,
Sessions with time: 2042,
Total sessions: 2298,
Total users from 1st day: 40,
Retained 30th day: 1,
Retained 1st day: 21,
Total monthly users: 4252,
Total users from 7th day: 35,
Total daily users: 734,
Total users from 14th day: 42
}
Total first-time users
Total first-time users
Total session length
Total session length
Retained 14th day
Numerator for the metric 14-day retention
Total users from 3rd day
Denominator for the metric 3-day retention
Total weekly users
Weekly Active Users
Retained 3rd day
Numerator for the metric 3-day retention
Retained 7th day
Numerator for the metric 7-day retention
Total users from 30th day
Denominator for the metric 30-day retention
Total sessions
Total Sessions
Total users from 1st day
Denominator for the metric 1-day retention
Retained 30th day
Numerator for the metric 30-day retention
Retained 1st day
Numerator for the metric 1-day retention
Total monthly users
Monthly active users
Total users from 7th day
Denominator for the metric 7-day retention
Total daily users
Daily active users
Total users from 14th day
Denominator for the metric 14-day retention
Exporting Leanplum in-app message events to other analytics providers
This guide describes different methods to export Leanplum In-app message events to other analytics providers. The events will be exported on a per-user basis (as identified by their user id on Leanplum) so that you will be able to carry out analysis on your users on your Provider.
For in-app messages, there are two kinds of messaging events to track: Views and Accepts.
Method 1: Track in the app
Modify the Leanplum in-app message templates to send tracking events via your Provider's SDK whenever a user opens an in-app message or clicks a call-to-action.
Method 2: exportUsers
This method is suitable if you need to export data on messaging campaigns after the campaign is completed, or at a specific point during the campaign's life.
You can run an API query to export the list of users who have been sent a particular message.
For example, to see a list of all users who have been served in-app Message ID 5181850935361536:
- Create an Audience in the Dashboard segmenting the users who have viewed the above message.
- Prepare and run the API request below, which would return the list of all users who accepted Message ID 5181850935361536 to date:
https://api.leanplum.com/api?appId=YOUR_APP_ID&clientKey=YOUR_DATA_EXPORT_KEY&apiVersion=1.0.6&action=exportUsers&audience=AUDIENCE_NAME - The response will return a jobId which you could use to run the getExportResults method and obtain the CSV file with the data.
See more on exportUsers.
Method 3: exportData
You can use exportData to continuously export all Leanplum raw data in JSON format. This gives you maximum flexibility, as the data will contain all event and messaging data for all of your users. You may need resources from your data warehouse team to ingest and process the raw data, and then upload to your Provider.
The raw data can be pulled from Leanplum using the exportData API call, or you can configure Leanplum to automatically push the raw data to an AWS S3 bucket of your choice.
See more on exportData.
Exporting Leanplum A/B test info to other analytics providers
This guide describes the method in which to get a/b test information (the users in the test and which variant they fall within) into other analytics providers.
At the beginning of each user's app session, Leanplum assigns a Variant ID for each A/B test that is applicable for the user. For example, suppose a user qualifies for two tests:
- Test A
- Test B
Suppose the user is assigned "Variant 1" on "Test A" and "Control" on "Test B". Leanplum will give the user two variant IDs: 6331369890775040 and 5702495255396352.
The app only has the Leanplum Variant IDs for several reasons:
- It is more efficient for the app to only receive the numeric IDs, because they are more compact than receiving the entire test name and variant name.
- The test names and variant names can be changed later in the dashboard, which would cause data to not match. The numeric Variant IDs are persistent and guaranteed unique.
- For security, we do not want the end user to be able to see the names of the A/B tests or variants.
Two export options
Use Leanplum's API to extract users' variant ID for Test A and Test B. Use the exportUsers call.
The SDK start call response returns the variables that have changed for the user - Have the SDK pass this response to your backend. Start call response.
Translating IDs into readable formats
Next you may want the ability to map these numeric variant ids into human-readable names.
Leanplum provides an API that gives you information on translating Variant IDs back into human-readable names. The most comprehensive API call is getAbTests.
This will return information about all A/B Tests and Variants for a specific app. From this information, you can determine that 6331369890775040 is "Variant 1" in "Test A" and 5702495255396352 is "Control" in "Test B".
Export Raw Data via Python Scripts [Sample: API]
See examples for exporting the raw data via the api as well as the S3 buckets.
The dataExport script performs the following two calls: exportData and getExportResults.
The exportData portion of the script takes the input of a startDate, AppId, and exportKey.
- The AppId and exportKey can be found in Keys and Settings.
- The startDate is the date in which you hope to extract data.
The dataExport call returns a jobId that will be needed to perform the getExportedResults. Once you’ve inputted the keys, then the script will perform the API call. The script will print a message until a jobId is returned.
The script will then perform the getExportResults call, and save the data in files titled with the startDate, dataExport, and file number (large data results will break it into multiple files).
For example, if you did a dataExport for November 18th, 2015 then the file will be labeled as 20151118dataExport0.
Accessing content via the API
To get the variables that are being presented to a user, use the getVars API call. In order to perform the call, you'll need the following:
clientKey
String
Production or development key in app settings
userId
String
userId of the user that you want to see the variables for
devMode
Boolean
True or false depending on which client key is used
The API call will appear as such:
http://api.leanplum.com/api?appId=APP_ID_HERE&clientKey=DEV_OR_PROD_KEY_HERE&apiVersion=1.0.6&action=getVars&userId=USERID_HERE&includeDefaults=true&devMode=TRUE_OR_FALSE
Tracking analytics data via API
In order to send a message that is triggered from an event coming from your server, you will need to track the data via an API call in Leanplum. For example, if a user completes a purchase from your store, you may want to track an event that includes the estimated arrival time as the parameter. To ensure proper handling of time zones, use ISO 8601 format for all date/time parameters.
For example, on the iOS SDK:
[Leanplum track:@"PurchaseBook" withParameters:@{@"ArrivalDateTime": @"2015-08-10T21:50:00+00:00", @"BookTitle": @”Emma"}];
And here is the equivalent for the REST API:
https://api.leanplum.com/api?appId=YOUR_APP_ID&clientKey=YOUR_CLIENT_KEY&action=track&apiVersion=1.0.6&userId=USER_ID&event=PurchaseBook¶ms={"ArrivalDateTime":"2015-08-10T13:30:00+00:00","BookTitle": "Emma"}&allowOffline=true
And then you can configure your messaging campaign as follows:
This will send the push at 2015-08-09 13:30 (exactly 1 day before the ArrivalDateTime parameter).
Interpreting metrics in raw data export files
How to find events based on IDs in data exports
You can use exportData with the getExportResults method to export raw sessions data into downloadable files. Once your download is complete, your data will either be in JSON or CSV format, depending on the export settings you chose.
Events in exports are formatted differently than within the Leanplum dashboard. The event information in the data export is denoted by an event name and the unix timestamp related to that event. Each of these events is captured with a timestamp, a floating point unix time stamp representing the time at which the user triggered the event (view or action). For example:
{
"time": 1.462301269315E9,
"name": <EventName>
}
See below for more on how to interpret this data and how it relates to the Leanplum dashboard.
Message events
In a raw data export, each event/metric tracked for messages in Leanplum includes the messageId, a unique identifier for the message.
You can also find this message ID in the dashboard by looking at the ID number at the end of the URL when viewing a specific message in the composer.
The events that relate to a specific message will follow the format .m{messageId} in your raw data export. See below for descriptions and examples of the different message events in the raw data export.
Message Sent
Denotes a "sent" event, meaning the server attempted to send the message (push, webhook, or email) to the user.
.m(messageId)
".m6678706135760896"
Action
The engagement action the user took, or the message event after send (e.g. View or Accept for in-app messages, open for push). For a full list of events, see Leanplum events.
".m(messageId) <Action>"
".m6678706135760896 View"
".m6678706135760896 Accept"
For example:
{
"time": 1.462301269315E9,
"name": ".m6678706135760896 Accept"
}
Leanplum uses ID numbers for all messages, campaigns, and tests. This allows you change the name of a message without altering its associated Analytics and event data.
Campaign events
Each event/metric tracked for campaigns in Leanplum includes the campaignId, a unique identifier for the campaign. This campaign ID number corresponds to the value in the URL when viewing a specific Campaign in the composer (dashboard).
In your raw data export, campaign-related events will follow the format .c{campaignId}. See below for a description and some examples.
Campaign Enter / Exit
Denotes when a user entered and exited the campaign. For a full list of events, see Leanplum events.
.c(campaignId) <Action>
".c6349584728588288 Enter"
".c6349584728588288 Exit"
For example:
{
"time": 1.462301269315E9,
"name": ".c(6349584728588288) Enter"
}
A/B Test events
Each event tracked for A/B tests in Leanplum includes the abTestId, a unique identifier for the test that corresponds to the value in the URL when viewing it in the A/B test editor (dashboard).
A/B Test Impression
Denotes when a user enters into the test. For a full list of events tracked by default, see here.
.a(abTestIId)
".a6598034896453632"
For example:
{
"time": 1.462301269315E9,
"name": ".a(6598034896453632)"
}
Custom events
Each custom event tracked in your app will appear with the same name you set up your track call. For example, if you have an event called "Play Video," it will also be called "Play Video" in your data export.
Custom Event
Denotes the custom event tracked in your app by using a Leanplum.track call.
<CustomEventName>
"Play Video"
For example:
{
"time": 1.462301269315E9,
"name": "Play Video"
}
Calculate DAU with the raw data via Python
There are many reasons why you may want to validate the raw data - especially with Daily Active Users (DAU). In this guide, we will go over the isSession flag, possible discrepancies, and how to properly calculate the DAU.
isSession flag
In the raw data, there is an isSession flag that is a boolean variable. This flag is used in our backend to determine whether or not to include the user in the DAU calculation. If this variable is set to false, then the user is not included in DAU count.
This flag could be set as false if:
- Events are fired out of order
- The user has gone offline
- You are sending us offline events via our REST API
We don't include these users in the DAU count because they may not actually be interacting with the app — so they should not count as active users.
We disregard the isSession flag when it comes to tracking events because the event could be an API call for when the user was outside of the app (in that case, the user would not have a session but they still triggered an event).
To sum up, we only search for isSession is true for DAU counts. For all events, we disregard the flag, since the users don't have to have a real session to trigger some events.
Data from Offline users
When you request data from us, we can only return data that is available at that time. Keep in mind that users on offline devices are not able to send Leanplum data until they reconnect. So, there could be some instances of trailing data.
When these users come back online, their data will then be batched over to Leanplum in that moment. Users' data can appear on the Leanplum server up to 7 days after the day of the event depending on internet connectivity of the user and when they return to the app.
Note: if you use our automated exporting feature via the s3 buckets, then offline data is accommodated for as the export ports over any "new data" received since the last export.
Python Script to get raw data
To see the actual code to count the DAU, you can use this script. The script is written assuming that you are also using the dataExport.py script though you are free to change the input of the function.
GDPR & CCPA overview
The GDPR (General Data Protection Regulation) took effect May 25, 2018, and its regulations apply to any company, person, or group that collects, processes, or otherwise handles the personal data of EU and UK residents.
Inspired by GDPR, California enacted its own consumer data protection regulations called the California Consumer Protection Act (CCPA). Effective January 1, 2020 this act creates new consumer rights for personal data collected from California residents.
Leanplum and you
GDPR
The GDPR defines two different types of organizations who have to follow the new regulations: data controllers and data processors.
- Data controllers determine why and how personal data is processed. As a Leanplum customer, your organization is considered a data controller.
- Data processors process user data on behalf of the controller. Leanplum processes user data on your behalf, which makes us a data processor.
As a processor, we provide the technical capabilities and organizational processes that will allow you, our customers, to maintain the rights of your EU and UK users while using our product.
CCPA
With CCPA, Leanplum is a Service Provider to our customers, Businesses. Consumer rights to erasure, objection, access & portability can be fulfilled via the same APIs created for GDPR. The right to rectification is currently absent from CCPA.
See below for some common tasks to help you remain GDPR and CCPA compliant while using Leanplum.
Informing your end users
As a data controller, you have to inform your end-users about the personal data you collect from them and their rights surrounding this data. GDPR & CCPA lay out several requirements for what you must inform your end-users, and it’s up to you to provide the information in a transparent, accessible way.
For more details on how Leanplum handles and protects your users’ data, refer to the security information in your contract’s data processing addendum.
User consent for data collection
Under GDPR & CCPA, users must explicitly opt-in to data collection before you start tracking with Leanplum. The GDPR has some stringent requirements for how and when to give users the option to consent for data collection, so make sure you are aware of these regulations.
To prevent data collection through Leanplum, do not call leanplum.start() until after the user opts-in for data collection. You can also offer separate opt-out prompts for different messaging channels in Leanplum. Giving users more options to control their app experience might encourage them to consent (opt-in) to data collection.
For example, if a user opts-in to data collection in general, but not to email or push, your app should call leanplum.start and the opt-out methods for the push and email channels. See the unsubscribe params in setUserAttributes for more info.
Block data collection and processing
If a user objects to data processing, you can prevent Leanplum from collecting and processing data for that user with the block API request.
block will stop Leanplum from collecting data for that user moving forward. In order to ensure that Leanplum does not process this user’s old data, we will delete all of their data from our systems.
See block for more details.
Erasing user data
Under GDPR & CCPA, data subjects have the right to request the deletion or removal of personal data.
To delete a user’s data from Leanplum, you can use the deleteUser request, which will delete all attributes for that user. You can also use this call to delete a user's sessions data.
See deleteUser for more information.
If you export Leanplum data into S3 buckets or other backup locations, you are responsible for handling GDPR & CCPA requests in that data.
Data access and portability
Users also have the right to request a copy of their personal data in a human or machine-readable format. Both GDPR and CCPA specify that data subjects can obtain and reuse their personal data for their own purposes (for example, to create an account with a competing service).
To receive a copy of a user’s data, you can use the exportUser request. This will return all user attributes.
More coming soon on requesting your users’ data from Leanplum.
Rectify user data
As a data controller, GDPR requires you to give users the ability to correct personal data if they feel it is inaccurate or incomplete. In Leanplum, this includes user location, user attributes, and device attributes.
Use the setUserAttributes API request to change a user's attributes or location data in Leanplum. You can also change device-specific attributes using setDeviceAttributes.
Additional support
If you are unsure of how to use some of the methods or processes above, contact [email protected] for assistance. We are happy to answer any questions on how to use our platform. Of course, please do not mistake this for legal advice. If you have legal questions about the GDPR or CCPA, we urge you to consult with your lawyer.
You can also find some general guidelines for using the Leanplum API here.

































