ChangelogEdit pageSTAFFOpen dashboard

Segments and events

Tag devices with segments for campaign targeting, record your own events, and listen to what Bubbl does as it happens.

Segments#

Segments are tags your app puts on a device, such as vip or newsletter, so campaigns in the dashboard can target them. They're created by your app through the SDK, never in the dashboard.

KOTLIN
Bubbl.setSegments(listOf("vip", "newsletter"))
SWIFT
Bubbl.setSegments(["vip", "newsletter"])
DART
await Bubbl.setSegments(['vip', 'newsletter']);
TS
Bubbl.setSegments(['vip', 'newsletter']);
  • setSegments replaces the device's segments: pass the whole list each time, and an empty list to clear them.
  • Up to 100 segments, each up to 255 characters. Bubbl trims them and drops empty ones and duplicates.
  • To set them from the first launch, pass segments in BubblOptions at start.
  • Bubbl keeps them on the device and sends them when it can, so it's fine to call offline.

In the dashboard, a segment appears under Company › Segments the first time a device reports it, with its device count and the campaigns that use it. Owners and admins can give it a readable label or hide it there. See Segments.

Custom events#

track records an event of your app's own, with optional properties, and sends it with Bubbl's event queue.

KOTLIN
Bubbl.track("purchase.completed", mapOf("total" to 12.5, "currency" to "GBP"))
SWIFT
Bubbl.track("purchase.completed", properties: ["total": 12.5, "currency": "GBP"])
DART
await Bubbl.track('purchase.completed', {'total': 12.5, 'currency': 'GBP'});
TS
Bubbl.track('purchase.completed', { total: 12.5, currency: 'GBP' });
  • Name: 1 to 100 characters from A–Z a–z 0–9 . _ : -. An event with any other name isn't sent, and Bubbl logs a warning.
  • Properties: up to 50, flat: text, numbers, true or false, or null. Anything else (a list, a nested object) is left out.
  • Events are only recorded while Bubbl is active: not before setConsent(true) with requireConsent, and not after an opt-out.

Where your events show in the dashboard#

Bubbl keeps each event against the device that sent it, with its name, its properties and when it happened. In the dashboard you see them in two places:

  • Active users: open a device, and its Recent activity lists its latest events alongside geofence, notification and delivery events. A custom event shows as custom. followed by its name.
  • The activity log: View full log → on a campaign's page opens it. It lists every device event, and a custom event has its name in the Type column.

An event also counts its device as active for Active users. Properties are stored but not shown anywhere in the dashboard, and there's no report, chart, segment or campaign rule built on custom events. Export workspace data (Company settings) includes them, without their properties. Keep location events for doesn't delete them; it only removes any position they carry.

Listening to events#

addEventListener tells your app what Bubbl does as it happens, on the main thread: each step of a notification's life, geofences entered and left, and errors. These are for your own use, such as your analytics or UI; Bubbl records its own for your campaign reports.

What happenedAndroid (BubblEvent.)iOS (BubblEvent.)FlutterReact Native (type)
A notification arrived, before it's shownNotificationReceived.notificationReceivedBubblNotificationReceivednotification.received
It's on screenNotificationDisplayed.notificationDisplayedBubblNotificationDisplayednotification.displayed
Opened from its system notificationNotificationOpened.notificationOpenedBubblNotificationOpenednotification.opened
Call to action tappedNotificationCtaClicked.notificationCtaClickedBubblNotificationCtaClickednotification.cta_clicked
Closed without actingNotificationDismissed.notificationDismissedBubblNotificationDismissednotification.dismissed
Media shown or startedMediaViewed.mediaViewedBubblMediaViewedmedia.viewed
Video or audio played to the endMediaCompleted.mediaCompletedBubblMediaCompletedmedia.completed
Survey startedSurveyStarted.surveyStartedBubblSurveyStartedsurvey.started
Survey answers sentSurveySubmitted.surveySubmittedBubblSurveySubmittedsurvey.submitted
Entered a geofenceGeofenceEntered.geofenceEnteredBubblGeofenceEnteredgeofence.entered
Left a geofenceGeofenceExited.geofenceExitedBubblGeofenceExitedgeofence.exited
Something went wrong that Bubbl couldn't fixError.errorBubblErrorerror
The device credential was refusedCredentialRejected.credentialRejectedBubblCredentialRejectedcredential.rejected

The notification events carry the BubblMessage (message). The geofence events carry locationId, the dashboard location's ID. The error event carries a message string, which diagnostics also keep as lastError.

Always handle unknown eventsNew event types arrive in minor releases, so give your when an else branch and your switch a default case, and ignore events you don't know. The Kotlin and Dart event types are open, so the compiler insists on it in a when or switch expression; elsewhere, add it yourself.
KOTLIN
val listener = BubblEventListener { event ->
    when (event) {
        is BubblEvent.NotificationDisplayed -> analytics.log("bubbl_shown", event.message.id)
        is BubblEvent.GeofenceEntered -> analytics.log("bubbl_entered", event.locationId)
        else -> {}
    }
}
Bubbl.addEventListener(listener)
// later
Bubbl.removeEventListener(listener)
SWIFT
let token = Bubbl.addEventListener { event in
    switch event {
    case .notificationDisplayed(let message): analytics.log("bubbl_shown", message.id)
    case .geofenceEntered(let locationId): analytics.log("bubbl_entered", locationId)
    default: break
    }
}
// later
Bubbl.removeEventListener(token)
DART
final subscription = Bubbl.addEventListener((event) {
  switch (event) {
    case BubblNotificationDisplayed(:final message):
      analytics.log('bubbl_shown', message.id);
    case BubblGeofenceEntered(:final locationId):
      analytics.log('bubbl_entered', locationId);
    default:
      break;
  }
});
// later
await Bubbl.removeEventListener(subscription);
TS
const subscription = Bubbl.addEventListener((event) => {
  switch (event.type) {
    case 'notification.displayed': analytics.log('bubbl_shown', event.message.id); break;
    case 'geofence.entered': analytics.log('bubbl_entered', event.locationId); break;
    default: break;
  }
});
// later
subscription.remove();

In Flutter, Bubbl.events is a broadcast Stream<BubblEvent> of the same events, if you prefer a stream. On Android, keep the listener object to remove it; on iOS, keep the BubblEventToken that addEventListener returns (Swift closures have no identity to remove them by).

See BubblEvent for each event's fields.