> ## Documentation Index
> Fetch the complete documentation index at: https://docs.reelevant.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Mobile Collection

> Collect behavioural events from Android, iOS, and Flutter apps with the Reelevant mobile SDKs

## Overview

The mobile SDKs build events with typed helpers and post them to the same collector as the web tag. One instance handles collection and [personalisation](/developer-docs/mobile-integration/sdk/overview), so identity is shared.

<CodeGroup>
  ```kotlin Android theme={"theme":{"light":"github-light","dark":"github-dark"}}
  val rlvt = ReelevantSDK(
      context = applicationContext,
      companyId = "your-company-id",
      datasourceId = "your-datasource-id"
  )

  rlvt.setUser("user@example.com")
  rlvt.send(rlvt.productPage("SKU-12345", mapOf("locale" to "EN-GB")))
  ```

  ```swift iOS theme={"theme":{"light":"github-light","dark":"github-dark"}}
  let configuration = ReelevantAnalytics.Configuration(
      companyId: "your-company-id",
      datasourceId: "your-datasource-id"
  )
  let rlvt = ReelevantAnalytics.SDK(configuration: configuration)

  rlvt.setUser(userId: "user@example.com")
  rlvt.send(event: ReelevantAnalytics.EventBuilder.product_page(
      productId: "SKU-12345",
      labels: ["locale": "EN-GB"]
  ))
  ```

  ```dart Flutter theme={"theme":{"light":"github-light","dark":"github-dark"}}
  final rlvt = ReelevantAnalytics(
    companyId: 'your-company-id',
    datasourceId: 'your-datasource-id',
  );

  await rlvt.setUser('user@example.com');
  await rlvt.send(rlvt.productPage(id: 'SKU-12345', labels: {'locale': 'EN-GB'}));
  ```
</CodeGroup>

Events are built and sent in two steps on every platform: a builder returns an event, `send()` posts it. Building an event has no side effect, so you can enrich or discard it before sending.

Installation instructions for each platform are on the [Android](/developer-docs/mobile-integration/sdk/android), [iOS](/developer-docs/mobile-integration/sdk/ios), and [Flutter](/developer-docs/mobile-integration/sdk/flutter) pages.

## Event builders

| Event           | Android                                       | iOS                                                      | Flutter                                     |
| --------------- | --------------------------------------------- | -------------------------------------------------------- | ------------------------------------------- |
| `page_view`     | `pageView(labels)`                            | `EventBuilder.page_view(labels:)`                        | `pageView(labels:)`                         |
| `product_page`  | `productPage(id, labels)`                     | `EventBuilder.product_page(productId:labels:)`           | `productPage(id:labels:)`                   |
| `product_hover` | `productHover(id, labels)`                    | `EventBuilder.product_hover(productId:labels:)`          | `productHover(id:labels:)`                  |
| `category_view` | `categoryView(id, labels)`                    | `EventBuilder.category_view(categoryId:labels:)`         | `categoryView(id:labels:)`                  |
| `brand_view`    | `brandView(id, labels)`                       | `EventBuilder.brand_view(brandId:labels:)`               | `brandView(id:labels:)`                     |
| `add_cart`      | `addCart(ids, labels)`                        | `EventBuilder.add_cart(ids:labels:)`                     | `addCart(ids:labels:)`                      |
| `purchase`      | `purchase(ids, totalAmount, transId, labels)` | `EventBuilder.purchase(ids:totalAmount:labels:transId:)` | `purchase(ids:totalAmount:labels:transId:)` |
| custom          | `custom(name, labels)`                        | `EventBuilder.custom(name:labels:)`                      | `custom(name:labels:)`                      |

<Warning>
  Argument order differs between platforms for `purchase` — Android takes `transId` before `labels`, iOS and Flutter take it last. Rely on named arguments where the language allows it.
</Warning>

Labels are `String` key/value pairs stored as queryable labels on the event. Use them for the dimensions you filter on in a Workflow — locale, store, app version — not for high-cardinality data.

## Identity

`setUser()` stores the identity on device (SharedPreferences on Android, UserDefaults on iOS, shared preferences on Flutter) and sends an `identify` event when the value changes. Call it after login and after restoring a session at launch — repeated calls with the same value are no-ops.

Until then, events carry only the anonymous device identity (`tmpId`): the advertising ID on Android when ad tracking is allowed, `identifierForVendor` on iOS, otherwise a random identifier generated once and persisted.

```kotlin theme={"theme":{"light":"github-light","dark":"github-dark"}}
// Android — restore identity at launch, before any event is sent
class ReelevantHolder(context: Context, private val session: SessionStore) {
    val sdk = ReelevantSDK(
        context = context,
        companyId = BuildConfig.RLVT_COMPANY_ID,
        datasourceId = BuildConfig.RLVT_DATASOURCE_ID
    )

    suspend fun restoreIdentity() {
        val userId = session.currentUserId() ?: return
        sdk.setUser(userId)
    }
}
```

iOS exposes `ReelevantAnalytics.clearStorage()` to drop the stored user ID, temporary ID, and retry queue — call it on logout when the device is shared.

## Screen context

Every event carries a `url` field. In apps it defaults to `unknown`, so set it on navigation to be able to filter events by screen:

<CodeGroup>
  ```kotlin Android theme={"theme":{"light":"github-light","dark":"github-dark"}}
  rlvt.setCurrentURL("app://catalogue/product/SKU-12345")
  ```

  ```swift iOS theme={"theme":{"light":"github-light","dark":"github-dark"}}
  rlvt.setCurrentURL(url: "app://catalogue/product/SKU-12345")
  ```

  ```dart Flutter theme={"theme":{"light":"github-light","dark":"github-dark"}}
  rlvt.setCurrentURL('app://catalogue/product/SKU-12345');
  ```
</CodeGroup>

Use a stable scheme and path structure — the field is indexed as text, so consistent paths keep Workflow filters simple.

## Delivery guarantees

All three SDKs behave identically on failure:

| Behaviour       | Detail                                                                                                 |
| --------------- | ------------------------------------------------------------------------------------------------------ |
| **Retry queue** | Failed requests are persisted on device and retried every 60 seconds, so events survive app restarts.  |
| **Expiry**      | Queued events older than 15 minutes are dropped instead of being retried.                              |
| **Timestamps**  | Every event carries the client timestamp of the original call, so retried events keep their real time. |

`send()` does not report transport failures to the caller — they are logged and queued. It can still throw when it is called before the SDK finished initialising its device identity, so wrap calls made early in the app lifecycle:

```dart theme={"theme":{"light":"github-light","dark":"github-dark"}}
// Flutter — a purchase event must never break checkout
try {
  await rlvt.send(rlvt.purchase(
    ids: ['SKU-12345', 'SKU-67890'],
    totalAmount: 129.90,
    labels: {'store': 'FR-online'},
    transId: 'order-456',
  ));
} catch (error, stackTrace) {
  logger.warning('Reelevant purchase event failed', error, stackTrace);
}
```

## Related

* [Data collection overview](/developer-docs/data-collection/overview) — pipeline, identity, consent
* [Event reference](/developer-docs/data-collection/events-reference) — envelope, catalogue, validation rules
* [Mobile SDK](/developer-docs/mobile-integration/sdk/overview) — personalisation with the same instance
* [Website collection](/developer-docs/data-collection/web) — the web equivalent
