IAM Handlers

📘

This feature is available starting from iOS SDK 5.0.0 and Android SDK 6.0.0

If you are already using custom message templates and handlers, check the Migration docs: iOS, Android.

Leanplum provides powerful orchestration capabilities and flexibility in triggers. We now give full control over in-app messages to the app developer as well.
This way application developers can decide what and when to be shown and what the in-app message behavior is. This is especially useful when the app shows in-app messages from multiple sources, including Ads sources or cinematics.
You can also get notified when a message is about to be shown, has been presented, dismissed, or the user interacts with it.

In-app Display control

You can implement a handler to get notified before an in-app message is displayed. It is executed per message to decide whether to Show, Discard or Delay it.

  • Show - default behavior, the in-app is displayed.

  • Discard - discards the message, it will not be displayed.

  • Delay - delay the message for the desired interval in seconds.

  • Delay Indefinitely - the SDK waits for the app to manually tell it to display a message from the delayed messages queue.

This handler is called every time a message is to be presented - when the message is triggered, or when delayed messages are about to be shown. This way you can delay a message multiple times, or delay it and then decide to discard it.

The handler provides an ActionContext of the message, containing information like message type, template, parameters etc.

// Function
/// Called per message to decide whether to show, discard or delay it.
@objc public func shouldDisplayMessage(_ callback: ((ActionContext) -> MessageDisplayChoice)?)

// Example
ActionManager.shared.shouldDisplayMessage { actionContext in
     // return the desired option
     return .show()
     return .discard()
     return .delay(seconds: 30)
     return .delayIndefinitely()
}

// remove handler
ActionManager.shared.shouldDisplayMessage(nil)
// Implement MessageDisplayController interface
interface MessageDisplayController {

  /**
   * Called per message to decide whether to show, discard or delay it.
   */
  fun shouldDisplayMessage(action: ActionContext): MessageDisplayChoice?

  /**
   * Called when there are multiple messages to be displayed.
   * We can order or remove any of them that we don't want to present.
   */
  fun prioritizeMessages(actions: List<ActionContext>, trigger: ActionsTrigger?): List<ActionContext>
}

// Example
object MessageDisplayControllerObject : MessageDisplayController {
    override fun shouldDisplayMessage(action: ActionContext) =
      MessageDisplayChoice.show()
      MessageDisplayChoice.discard()
      MessageDisplayChoice.delay(30)
      MessageDisplayChoice.delayIndefinitely()
}
// set the controller
LeanplumActions.setMessageDisplayController(MessageDisplayControllerObject)
  
// remove the controller
LeanplumActions.setMessageDisplayController(null)

Trigger delayed messages

The messages/actions that you have chosen to delay indefinitely can be triggered by calling the function:

/// Triggers all postponed messages when indefinite time was used with `MessageDisplayChoice`
@objc public func triggerDelayedMessages()

ActionManager.shared.triggerDelayedMessages()
/**
 * Method will trigger postponed messages when indefinite time was used with
 * [MessageDisplayController.shouldDisplayMessage]
 */
@JvmStatic
fun triggerDelayedMessages()

LeanplumActions.triggerDelayedMessages()

In-app prioritization

If there are multiple messages that are triggered on the same trigger, you can use this handler to decide which message/s to be displayed.

If the handler is not implemented, the default behavior is the same as on previous versions - only the first message based on Priority is displayed.

This handler gives the opportunity to present multiple messages one after another, prioritize them differently, and choose which ones to be displayed.

Example

You have 3 messages triggered on User starts app, as follows:

  • Message 1 (M1) with priority 900
  • Message 2 (M2) with priority 1
  • Message 3 (M3) with priority 4

If the handler is not implemented, only M2 will be displayed.

If you do implement the handler, you will be provided with the action contexts of all the above messages, and the action trigger information, in this case, User starts app.

The action contexts parameter is an array in the following order, based on Priority:
[M2, M3, M1]

If you simply return the array of contexts, all 3 messages will be displayed one after another in the same order they are in the array - first M2, then M3, and last M1.

If the user interacts with any message and executes its Open or Accept action, that action is executed, and then the rest of the messages.

You can reorder the messages and return the following array [M1, M2, M3], changing the display priority. Now M1 will be displayed first, then M2 etc.

You can also return just one message, for example, M1:
[M1]
The rest of the messages are discarded and will not be presented.

At a glance

Messages are ordered by Priority. They can be reordered or removed if desired. Removed messages will not be presented. The in-apps will be presented one after another in the order returned.

Note that for each action, the shouldDisplayMessage handler will be called as well.

/// Called when there are multiple messages to be displayed. Messages are ordered by Priority.
/// Messages can be reordered or removed if desired. Removed messages will not be presented.
/// Messages will be presented one after another in the order returned.
///
/// - Note: If this function is not implemented, the first message is presented only.
///
/// - Parameters:
///     - callback: contexts - messages' contexts and trigger - the action trigger that triggered the messages
///
/// - Returns: the messages that should be presented in that order
@objc public func prioritizeMessages(_ callback: ((_ contexts: [ActionContext], _ trigger: ActionsTrigger?) -> [ActionContext])?)     

// Example
ActionManager.shared.prioritizeMessages { contexts, trigger in
    // return the contexts to be displayed
    return contexts
}

// remove handler
ActionManager.shared.prioritizeMessages(nil)
// Implement MessageDisplayController interface
interface MessageDisplayController {

  /**
   * Called per message to decide whether to show, discard or delay it.
   */
  fun shouldDisplayMessage(action: ActionContext): MessageDisplayChoice?

  /**
   * Called when there are multiple messages to be displayed.
   * We can order or remove any of them that we don't want to present.
   */
  fun prioritizeMessages(actions: List<ActionContext>, trigger: ActionsTrigger?): List<ActionContext>
}

// Example
object MessageDisplayControllerObject : MessageDisplayController {
  override fun prioritizeMessages(
    actions: List<ActionContext>,
    trigger: ActionsTrigger?) = actions // the actions to be presented
}

// set the controller
LeanplumActions.setMessageDisplayController(MessageDisplayControllerObject)
  
// remove the controller
LeanplumActions.setMessageDisplayController(null)

In-app event callbacks

Get notified when a message is displayed, dismissed, or interacted with. Those are valid for all types of in-app messages (default and custom).
All handlers provide the in-app action context.

  • onMessageDisplayed - called when the in-app is displayed

  • onMessageDismissed - called when the in-app was dismissed

  • onMessageAction - when action is executed from the in-app message - the action name is also passed

/// Called when the message is displayed.
@objc public func onMessageDisplayed(_ callback: @escaping (ActionContext) -> Void) 

/// Called when the message is dismissed.
@objc public func onMessageDismissed(_ callback: @escaping (ActionContext) -> Void) 

/// Called when a message action is executed.
@objc public func onMessageAction(_ callback: @escaping (_ actionName: String, _ context: ActionContext) -> Void)

// Example
ActionManager.shared.onMessageDisplayed { context in
    // code
}

ActionManager.shared.onMessageDismissed { context in
    // code
}

ActionManager.shared.onMessageAction { actionName, context in
    // code
}

// set the closures to nil to remove them
// Implement MessageDisplayListener interface
interface MessageDisplayListener {
  /**
   * Called when the message is displayed.
   */
  fun onMessageDisplayed(action: ActionContext)

  /**
   * Called when the message is dismissed.
   */
  fun onMessageDismissed(action: ActionContext)

  /**
   * Called when the message is clicked
   */
  fun onActionExecuted(name: String, action: ActionContext)
}

// Example
object MessageDisplayListenerObject : MessageDisplayListener {
  override fun onMessageDisplayed(action: ActionContext) {
    // code
  }

  override fun onMessageDismissed(action: ActionContext) {
    // code
  }

  override fun onActionExecuted(name: String, action: ActionContext) {
    // code
  }
}

// set the listener
LeanplumActions.setMessageDisplayListener(MessageDisplayListenerObject)

// remove the listener
LeanplumActions.setMessageDisplayListener(null)

Pause in-app messages

You can pause displaying in-app messages. During the pause, messages that are triggered are persisted for the session and will be displayed once the execution is resumed.

// Pause
ActionManager.shared.isPaused = true

// Resume
ActionManager.shared.isPaused = false
// Pause
LeanplumActions.setQueuePaused(true)

// Resume
LeanplumActions.setQueuePaused(false)

Disable in-app messages

Disable showing in-app messages. Messages triggered during that time will be discarded.

// Disable
ActionManager.shared.isEnabled = false

// Enable
ActionManager.shared.isEnabled = true
// Disable
LeanplumActions.setQueueEnabled(false)

// Enable
LeanplumActions.setQueueEnabled(true)

Other Configurations

The new ActionManager also provides additional configurations.

  • resumeOnEnterForeground - default True
    The ActionManager will display any actions still queued to be displayed when the app is resumed.

  • dismissOnPushArrival - default True
    Keep or dismiss in-app message that is currently presented when a push notification is opened. If kept, the action from the push notification will go into the queue and will be presented after in-app dismissal, otherwise
    the in-app is dismissed and the push notification's action is presented.

ActionManager.shared.configuration = 
ActionManager.Configuration(dismissOnPushArrival: false, resumeOnEnterForeground: false)
LeanplumActions.setContinueOnActivityResumed(false)

LeanplumActions.setDismissOnPushOpened(false)