
An inline in-app message is a component that you embed directly into your app's view hierarchy — for example, a banner between list sections, a tile on the home screen, a menu, or even an entire view. They allow you to manipulate content in the application without changing the code of the app.

## Trigger modes
---

Inline in-app campaigns support three trigger modes, configured in the campaign settings:

- **Automatic** — the in-app message is rendered automatically when a view with the associated placement (container ID) appears on the screen.
- **On demand** — the in-app message is rendered when the application calls the SDK method.
- **Event** — the in-app message is rendered when a specified event occurs in the application.

In terms of the SDK behavior:
- For **Automatic**, the SDK shows the content as soon as the component is added to the view hierarchy.
- For **On demand**, the application calls [`render()`](/developers/mobile-sdk/method-reference/android/campaigns#render-inline-in-app-view) to trigger content rendering.
- For **Event**, if a component for the placement is already embedded, the SDK updates its content. Otherwise, the SDK creates a new component and delivers it through the global listener/delegate.

## Multiple views and campaign priority
---

Several `InlineInAppView` instances can share the same placement key. Each instance receives the campaign independently — `inApp.show` and `inApp.discard` events are reported per view, while capping is counted once per display.

When more than one campaign matches a placement, the campaign with the highest priority wins (lower number = higher priority). A campaign that is already displayed is not replaced by another campaign of equal or lower priority.

## Controlling behavior and actions
---

Inline in-app messages use two types of callbacks:

- **Per-component listener/delegate** handles the lifecycle of an individual component: loading, updating, size changes, and removal requests. You set it on each `InlineInAppView` instance.
- **Global listener/delegate** handles action callbacks: URL, deeplink, custom action, custom method; and delivers SDK-created components for event-based triggers. You set it once at the application level.

You can handle the per-component lifecycle using:
- [OnInlineInAppViewListener](/developers/mobile-sdk/listeners-and-delegates/android-listeners#on-inline-in-app-view-listener) methods for Android
- [InlineInAppViewDelegate](/developers/mobile-sdk/listeners-and-delegates/ios-delegates#inline-in-app-view-delegate) methods for iOS

You can handle actions and event-based delivery using:
- [OnInlineInAppListener](/developers/mobile-sdk/listeners-and-delegates/android-listeners#on-inline-in-app-listener) methods for Android
- [InjectorInlineInAppMessageDelegate](/developers/mobile-sdk/listeners-and-delegates/ios-delegates#injector-inline-in-app-message-delegate) methods for iOS

See the following code samples for the per-component delegate/listener:


<div class="content-tabs code-tabs" data-tab-group="tabgrp-4123">
<div class="tab-buttons"><button class="tab-button" data-tab-id="tabgrp-4123-0" data-tab-group="tabgrp-4123" data-tab-active="true">Java</button><button class="tab-button" data-tab-id="tabgrp-4123-1" data-tab-group="tabgrp-4123">Kotlin</button><button class="tab-button" data-tab-id="tabgrp-4123-2" data-tab-group="tabgrp-4123">Swift</button></div>

<div class="tab-panel" data-tab-id="tabgrp-4123-0" data-tab-group="tabgrp-4123" data-tab-active="true">

```Java
inlineView.setOnInlineInAppViewListener(new OnInlineInAppViewListener() {
  // This method is called when the SDK starts processing a campaign for this component.
  @Override
  public void onProcessingStarted() {
    //...
  }

  // This method is called when the inline in-app message content is loaded for the first time.
  @Override
  public void onLoaded(InlineInAppMessageData data) {
    //...
  }

  // This method is called when the inline in-app message content is updated with a new campaign.
  @Override
  public void onUpdated(InlineInAppMessageData data) {
    //...
  }

  // This method is called when the inline in-app message content fails to load or render.
  @Override
  public void onFailed(InlineInAppMessageData data, String error) {
    //...
  }

  // This method is called when the in-app message content requests a size change
  // via the SRInApp.setComponentSize(width, height) JS method.
  @Override
  public void onSizeChanged(InlineInAppMessageData data, InlineInAppSize size) {
    //...
  }

  // This method is called when the in-app message content requests removal from the view hierarchy
  // via the SRInApp.close() or SRInApp.closeAndTrigger() JS method.
  @Override
  public void onRemove(InlineInAppMessageData data) {
    //...
  }
});
```

</div>

<div class="tab-panel" data-tab-id="tabgrp-4123-1" data-tab-group="tabgrp-4123">

```Kotlin
inlineView.setOnInlineInAppViewListener(object : OnInlineInAppViewListener {
  // This method is called when the SDK starts processing a campaign for this component.
  override fun onProcessingStarted() {
    //...
  }

  // This method is called when the inline in-app message content is loaded for the first time.
  override fun onLoaded(data: InlineInAppMessageData) {
    //...
  }

  // This method is called when the inline in-app message content is updated with a new campaign.
  override fun onUpdated(data: InlineInAppMessageData) {
    //...
  }

  // This method is called when the inline in-app message content fails to load or render.
  override fun onFailed(data: InlineInAppMessageData, error: String) {
    //...
  }

  // This method is called when the in-app message content requests a size change
  // via the SRInApp.setComponentSize(width, height) JS method.
  override fun onSizeChanged(data: InlineInAppMessageData, size: InlineInAppSize) {
    //...
  }

  // This method is called when the in-app message content requests removal from the view hierarchy
  // via the SRInApp.close() or SRInApp.closeAndTrigger() JS method.
  override fun onRemove(data: InlineInAppMessageData) {
    //...
  }
})
```

</div>

<div class="tab-panel" data-tab-id="tabgrp-4123-2" data-tab-group="tabgrp-4123">

```Swift
// MARK: - InlineInAppViewDelegate

// This method is called when the SDK starts processing a campaign for this component.
func snr_inlineInAppViewDidStartProcessing(view: InlineInAppView) {
  //...
}

// This method is called when the inline in-app message content is loaded for the first time.
func snr_inlineInAppViewDidLoad(view: InlineInAppView, data: InlineInAppMessageData) {
  //...
}

// This method is called when the inline in-app message content is updated with a new campaign.
func snr_inlineInAppViewDidUpdate(view: InlineInAppView, data: InlineInAppMessageData) {
  //...
}

// This method is called when the inline in-app message content fails to load or render.
func snr_inlineInAppViewDidFail(view: InlineInAppView, error: Error) {
  //...
}

// This method is called when the in-app message content requests a size change
// via the SRInApp.setComponentSize(width, height) JS method.
func snr_inlineInAppViewChangeSizeIsNeeded(view: InlineInAppView, data: InlineInAppMessageData, size: InlineInAppSize) {
  //...
}

// This method is called when the in-app message content requests removal from the view hierarchy
// via the SRInApp.close() or SRInApp.closeAndTrigger() JS method.
func snr_inlineInAppViewShouldBeRemoved(view: InlineInAppView, data: InlineInAppMessageData) {
  //...
}
```

</div>
</div>


See the following code samples for the global listener/delegate:


<div class="content-tabs code-tabs" data-tab-group="tabgrp-4124">
<div class="tab-buttons"><button class="tab-button" data-tab-id="tabgrp-4124-0" data-tab-group="tabgrp-4124" data-tab-active="true">Java</button><button class="tab-button" data-tab-id="tabgrp-4124-1" data-tab-group="tabgrp-4124">Kotlin</button><button class="tab-button" data-tab-id="tabgrp-4124-2" data-tab-group="tabgrp-4124">Swift</button></div>

<div class="tab-panel" data-tab-id="tabgrp-4124-0" data-tab-group="tabgrp-4124" data-tab-active="true">

```Java
Injector.setOnInlineInAppListener(new OnInlineInAppListener() {
  // This method is called when the SDK creates a ready-to-display component
  // after an event-based trigger for a placement with no existing host view.
  @Override
  public void onInlineInAppAvailable(InlineInAppView view, InlineInAppMessageData data) {
    //...
  }

  // This method is called when a per-message context is needed for rendering.
  @Override
  public Map<String, Object> onContextFromAppRequired(InAppMessageData data) {
    return new HashMap<>();
  }

  // This method is called when the SRInApp.openUrl(url) method is used in an in-app message.
  @Override
  public void onOpenedUrl(InlineInAppMessageData data, String url) {
    //...
  }

  // This method is called when the SRInApp.openDeeplink(url) method is used in an in-app message.
  @Override
  public void onOpenedDeepLink(InlineInAppMessageData data, String deepLink) {
    //...
  }

  // This method is called when the SRInApp.handleCustomAction(name, params) method is used in an in-app message.
  @Override
  public void onCustomAction(InlineInAppMessageData data, String name, Map<String, Object> params) {
    //...
  }

  // This method is called when the SRInApp.customMethod(name, params) method is used in an in-app message.
  // Resolve the completion exactly once — with success or failure.
  @Override
  public void onCustomMethod(String name, HashMap<String, Object> params, InlineInAppMessageData data, InAppCustomMethodCompletion completion) {
    //...
  }
});
```

</div>

<div class="tab-panel" data-tab-id="tabgrp-4124-1" data-tab-group="tabgrp-4124">

```Kotlin
Injector.setOnInlineInAppListener(object : OnInlineInAppListener {
  // This method is called when the SDK creates a ready-to-display component
  // after an event-based trigger for a placement with no existing host view.
  override fun onInlineInAppAvailable(view: InlineInAppView, data: InlineInAppMessageData) {
    //...
  }

  // This method is called when a per-message context is needed for rendering.
  override fun onContextFromAppRequired(data: InAppMessageData): MutableMap<String, Any> {
    return HashMap()
  }

  // This method is called when the SRInApp.openUrl(url) method is used in an in-app message.
  override fun onOpenedUrl(data: InlineInAppMessageData, url: String) {
    //...
  }

  // This method is called when the SRInApp.openDeeplink(url) method is used in an in-app message.
  override fun onOpenedDeepLink(data: InlineInAppMessageData, deepLink: String) {
    //...
  }

  // This method is called when the SRInApp.handleCustomAction(name, params) method is used in an in-app message.
  override fun onCustomAction(data: InlineInAppMessageData, name: String, params: Map<String, Any>) {
    //...
  }

  // This method is called when the SRInApp.customMethod(name, params) method is used in an in-app message.
  // Resolve the completion exactly once — with success or failure.
  override fun onCustomMethod(name: String, params: HashMap<String, Any>, data: InlineInAppMessageData, completion: InAppCustomMethodCompletion) {
    //...
  }
})
```

</div>

<div class="tab-panel" data-tab-id="tabgrp-4124-2" data-tab-group="tabgrp-4124">

```Swift
// MARK: - InjectorInlineInAppMessageDelegate

// This method is called when the SDK creates a ready-to-display component
// after an event-based trigger for a placement with no existing host view.
func snr_inlineInAppMessageDidBecomeAvailable(view: InlineInAppView, data: InlineInAppMessageData) {
  //...
}

// This method is called when a per-message context is needed for rendering.
func snr_inlineInAppMessageContextIsNeeded(data: InlineInAppMessageData) -> [AnyHashable: Any]? {
  return nil
}

// This method is called when the SRInApp.openUrl(url) method is used in an in-app message.
func snr_inlineInAppMessageHandledAction(data: InlineInAppMessageData, url: URL) {
  //...
}

// This method is called when the SRInApp.openDeeplink(url) method is used in an in-app message.
func snr_inlineInAppMessageHandledAction(data: InlineInAppMessageData, deepLink: String) {
  //...
}

// This method is called when the SRInApp.handleCustomAction(name, params) method is used in an in-app message.
func snr_inlineInAppMessageHandledCustomAction(data: InlineInAppMessageData, name: String, parameters: [AnyHashable: Any]) {
  //...
}

// This method is called when the SRInApp.customMethod(name, params) method is used in an in-app message.
// Resolve the completion exactly once — with success or failure.
func snr_inlineInAppMessageHandledCustomMethod(data: InlineInAppMessageData, name: String, parameters: [AnyHashable: Any], completion: InAppCustomMethodCompletion) {
  //...
}
```

</div>
</div>


## Placement-based integration
---

In this approach, you create a component for a known placement, attach a per-component delegate/listener, and embed the view in your UI.

1. Create the component with [`Injector.createInlineInAppView`](/developers/mobile-sdk/method-reference/android/campaigns#create-inline-in-app-view).
2. Set the per-component delegate/listener on the created view.
3. Add the view to your view hierarchy.
4. For campaigns with the automatic trigger, the SDK requests content as soon as the view is in the hierarchy. For on-demand campaigns, call [`render()`](/developers/mobile-sdk/method-reference/android/campaigns#render-inline-in-app-view).


<div class="admonition admonition-note"><div class="admonition-icon"><svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5"><path stroke-linecap="round" stroke-linejoin="round" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" /></svg></div><div class="admonition-body"><div class="admonition-content">

The component has no default size — the application must set its dimensions. The in-app message content is automatically pinned to all edges of the component.

</div></div></div>



<div class="content-tabs code-tabs" data-tab-group="tabgrp-4125">
<div class="tab-buttons"><button class="tab-button" data-tab-id="tabgrp-4125-0" data-tab-group="tabgrp-4125" data-tab-active="true">Java</button><button class="tab-button" data-tab-id="tabgrp-4125-1" data-tab-group="tabgrp-4125">Kotlin</button><button class="tab-button" data-tab-id="tabgrp-4125-2" data-tab-group="tabgrp-4125">Swift</button></div>

<div class="tab-panel" data-tab-id="tabgrp-4125-0" data-tab-group="tabgrp-4125" data-tab-active="true">

```Java
InlineInAppView inlineView = Injector.createInlineInAppView(this, "home_top_banner");
inlineView.setOnInlineInAppViewListener(viewListener);

FrameLayout container = findViewById(R.id.banner_container);
container.addView(inlineView, new FrameLayout.LayoutParams(
    FrameLayout.LayoutParams.MATCH_PARENT,
    FrameLayout.LayoutParams.MATCH_PARENT));
```

</div>

<div class="tab-panel" data-tab-id="tabgrp-4125-1" data-tab-group="tabgrp-4125">

```Kotlin
val inlineView = Injector.createInlineInAppView(this, "home_top_banner")
inlineView.setOnInlineInAppViewListener(viewListener)

val container: FrameLayout = findViewById(R.id.banner_container)
container.addView(inlineView, FrameLayout.LayoutParams(
    FrameLayout.LayoutParams.MATCH_PARENT,
    FrameLayout.LayoutParams.MATCH_PARENT))
```

</div>

<div class="tab-panel" data-tab-id="tabgrp-4125-2" data-tab-group="tabgrp-4125">

```Swift
guard let inlineView = Injector.createInlineInAppView(placementKey: "home_top_banner") else {
    return
}

inlineView.delegate = self
inlineView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(inlineView)

NSLayoutConstraint.activate([
    inlineView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
    inlineView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
    inlineView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
    inlineView.heightAnchor.constraint(equalToConstant: 100)
])
```

</div>
</div>


## Event-based integration
---

In this approach, a campaign is triggered by an event (sent through `Tracker.send`), not by embedding a view. The behavior depends on whether a component for the given placement is already in the view hierarchy:

- **Component already embedded** — the SDK updates the existing view's content. The per-component delegate/listener receives `onUpdated`/`snr_inlineInAppViewDidUpdate`. The global listener/delegate is not called.
- **No existing component** — the SDK creates a new `InlineInAppView` and delivers it through the global listener/delegate (`onInlineInAppAvailable`/`snr_inlineInAppMessageDidBecomeAvailable`). The application must embed the delivered view in the appropriate place.


<div class="content-tabs code-tabs" data-tab-group="tabgrp-4126">
<div class="tab-buttons"><button class="tab-button" data-tab-id="tabgrp-4126-0" data-tab-group="tabgrp-4126" data-tab-active="true">Java</button><button class="tab-button" data-tab-id="tabgrp-4126-1" data-tab-group="tabgrp-4126">Kotlin</button><button class="tab-button" data-tab-id="tabgrp-4126-2" data-tab-group="tabgrp-4126">Swift</button></div>

<div class="tab-panel" data-tab-id="tabgrp-4126-0" data-tab-group="tabgrp-4126" data-tab-active="true">

```Java
@Override
public void onInlineInAppAvailable(InlineInAppView view, InlineInAppMessageData data) {
    ViewGroup container = containerForPlacementKey(data.getPlacementKey());
    if (container == null) {
        return;
    }
    view.setOnInlineInAppViewListener(this);
    container.addView(view, new FrameLayout.LayoutParams(
        FrameLayout.LayoutParams.MATCH_PARENT,
        FrameLayout.LayoutParams.MATCH_PARENT));
}
```

</div>

<div class="tab-panel" data-tab-id="tabgrp-4126-1" data-tab-group="tabgrp-4126">

```Kotlin
override fun onInlineInAppAvailable(view: InlineInAppView, data: InlineInAppMessageData) {
    val container = containerForPlacementKey(data.placementKey) ?: return
    view.setOnInlineInAppViewListener(this)
    container.addView(view, FrameLayout.LayoutParams(
        FrameLayout.LayoutParams.MATCH_PARENT,
        FrameLayout.LayoutParams.MATCH_PARENT))
}
```

</div>

<div class="tab-panel" data-tab-id="tabgrp-4126-2" data-tab-group="tabgrp-4126">

```Swift
func snr_inlineInAppMessageDidBecomeAvailable(view: InlineInAppView, data: InlineInAppMessageData) {
    guard let container = container(forPlacementKey: data.placementKey) else {
        return
    }

    view.delegate = self
    view.translatesAutoresizingMaskIntoConstraints = false
    container.addSubview(view)

    NSLayoutConstraint.activate([
        view.topAnchor.constraint(equalTo: container.topAnchor),
        view.bottomAnchor.constraint(equalTo: container.bottomAnchor),
        view.leadingAnchor.constraint(equalTo: container.leadingAnchor),
        view.trailingAnchor.constraint(equalTo: container.trailingAnchor)
    ])
}
```

</div>
</div>


## Dynamic sizing
---

The in-app message content can read the current size of the hosting component by calling `SRInApp.getComponentSize()` from the JS interface. This returns an object with `width` and `height` properties in dp (CSS pixels).

The content can request a size change at any time by calling `SRInApp.setComponentSize(width, height)`. The SDK communicates this request through the per-component delegate/listener. The application is responsible for updating the component's layout.


<div class="content-tabs code-tabs" data-tab-group="tabgrp-4127">
<div class="tab-buttons"><button class="tab-button" data-tab-id="tabgrp-4127-0" data-tab-group="tabgrp-4127" data-tab-active="true">Java</button><button class="tab-button" data-tab-id="tabgrp-4127-1" data-tab-group="tabgrp-4127">Kotlin</button><button class="tab-button" data-tab-id="tabgrp-4127-2" data-tab-group="tabgrp-4127">Swift</button></div>

<div class="tab-panel" data-tab-id="tabgrp-4127-0" data-tab-group="tabgrp-4127" data-tab-active="true">

```Java
@Override
public void onSizeChanged(InlineInAppMessageData data, InlineInAppSize size) {
    if (inlineView == null) {
        return;
    }
    ViewGroup.LayoutParams layoutParams = inlineView.getLayoutParams();
    layoutParams.height = size.getHeightPx();

    ViewGroup parent = (ViewGroup) inlineView.getParent();
    if (parent != null) {
        TransitionManager.beginDelayedTransition(parent, new AutoTransition());
    }
    inlineView.setLayoutParams(layoutParams);
}
```

</div>

<div class="tab-panel" data-tab-id="tabgrp-4127-1" data-tab-group="tabgrp-4127">

```Kotlin
override fun onSizeChanged(data: InlineInAppMessageData, size: InlineInAppSize) {
    val view = inlineView ?: return
    val layoutParams = view.layoutParams
    layoutParams.height = size.heightPx

    (view.parent as? ViewGroup)?.let {
        TransitionManager.beginDelayedTransition(it, AutoTransition())
    }
    view.layoutParams = layoutParams
}
```

</div>

<div class="tab-panel" data-tab-id="tabgrp-4127-2" data-tab-group="tabgrp-4127">

```Swift
func snr_inlineInAppViewChangeSizeIsNeeded(view: InlineInAppView, data: InlineInAppMessageData, size: InlineInAppSize) {
    heightConstraint?.constant = CGFloat(size.heightPt)
    UIView.animate(withDuration: 0.2) {
        self.view.layoutIfNeeded()
    }
}
```

</div>
</div>


## Removing the view
---

When the in-app message content calls `SRInApp.close()` or `SRInApp.closeAndTrigger(action, params, label)` from the JS interface, the SDK sends an `inApp.discard` event (with `closedBy=user`). In the case of `closeAndTrigger`, the SDK additionally sends a custom event through Tracker. The SDK then requests removal through the per-component delegate/listener.

On Android, call [`release()`](/developers/mobile-sdk/method-reference/android/campaigns#release-inline-in-app-view) before removing the view from the hierarchy to clean up the WebView and unregister the component from the SDK.


<div class="content-tabs code-tabs" data-tab-group="tabgrp-4128">
<div class="tab-buttons"><button class="tab-button" data-tab-id="tabgrp-4128-0" data-tab-group="tabgrp-4128" data-tab-active="true">Java</button><button class="tab-button" data-tab-id="tabgrp-4128-1" data-tab-group="tabgrp-4128">Kotlin</button><button class="tab-button" data-tab-id="tabgrp-4128-2" data-tab-group="tabgrp-4128">Swift</button></div>

<div class="tab-panel" data-tab-id="tabgrp-4128-0" data-tab-group="tabgrp-4128" data-tab-active="true">

```Java
@Override
public void onRemove(InlineInAppMessageData data) {
    if (inlineView == null) {
        return;
    }
    inlineView.release();

    ViewGroup parent = (ViewGroup) inlineView.getParent();
    if (parent != null) {
        parent.removeView(inlineView);
    }
    inlineView = null;
}
```

</div>

<div class="tab-panel" data-tab-id="tabgrp-4128-1" data-tab-group="tabgrp-4128">

```Kotlin
override fun onRemove(data: InlineInAppMessageData) {
    inlineView?.release()
    (inlineView?.parent as? ViewGroup)?.removeView(inlineView)
    inlineView = null
}
```

</div>

<div class="tab-panel" data-tab-id="tabgrp-4128-2" data-tab-group="tabgrp-4128">

```Swift
func snr_inlineInAppViewShouldBeRemoved(view: InlineInAppView, data: InlineInAppMessageData) {
    heightConstraint?.constant = 0
    view.removeFromSuperview()
    if view === self.inlineView {
        self.inlineView = nil
    }
}
```

</div>
</div>

