Track events
Screen tracking, typed e-commerce events, custom events, super-properties and the offline-first event queue.
Before reading this, Concepts → Event model has the model — families of events, offline queue, batching. This guide is the how.
You'll learn:
- Screen views — when and how
- Typed e-commerce events — the full catalog
- Custom events — when typed events don't fit
- Super-properties — set once, ride every event
- Manual flush — and when you'd want one
- Inspecting the queue + session
1. Track a screen view
Call once per screen the user lands on. The campaigns engine uses screen
events to segment users (e.g. "show offer to users who hit Cart but not
Checkout in the last 24h").
import cx.swan.sdk.Swan
import cx.swan.sdk.SwanEvents
// Recommended: SwanEvents helper. Wire event name is "screen".
SwanEvents.screen(mapOf(
"screenName" to "ProductDetail",
"productId" to "SKU-100",
))
// Or: the equivalent low-level call
Swan.track("screen", mapOf(
"screenName" to "ProductDetail",
"productId" to "SKU-100",
))Pair with setCurrentScreenName so subsequent events get the current screen
as a super-property without you passing it per-event:
Swan.setCurrentScreenName("ProductDetail")SwanEvents.screen(attributes: ["screenName": "ProductDetail", "productId": "SKU-100"])
Swan.shared.setCurrentScreenName("ProductDetail")import SwanSDK from '@loyalytics/swan-react-native-sdk';
const sdk = SwanSDK.getCurrentInstance();
sdk?.screen({ screenName: 'ProductDetail', productId: 'SKU-100' });
sdk?.setCurrentScreenName('ProductDetail');Swan.screen({ screenName: 'ProductDetail', productId: 'SKU-100' });
Swan.setCurrentScreenName('ProductDetail');2. Typed e-commerce events
These exist because the Swan campaigns engine recognises these exact event
names and payload shapes and can target / segment on them. Free-form
customEvent("productViewed", ...) will land but won't be indexed as
e-commerce. Use the typed helpers for anything in the catalog below.
import cx.swan.sdk.SwanEvents
SwanEvents.productViewed(mapOf(
"id" to "SKU-100",
"name" to "Cotton tee",
"price" to 49.95,
"currency" to "AED",
"category" to "Apparel/Tops",
))
SwanEvents.checkoutStarted(mapOf(
"cartId" to "cart-abc",
"itemCount" to 3,
"totalValue" to 142.50,
"currency" to "AED",
))
SwanEvents.orderCompleted(mapOf(
"orderId" to "ORD-9001",
"totalValue" to 142.50,
"currency" to "AED",
"items" to listOf(
mapOf("id" to "SKU-100", "qty" to 1),
mapOf("id" to "SKU-200", "qty" to 2),
),
))SwanEvents.productViewed(attributes: [
"id": "SKU-100", "name": "Cotton tee",
"price": 49.95, "currency": "AED",
])
SwanEvents.checkoutStarted(attributes: [
"cartId": "cart-abc", "itemCount": 3,
"totalValue": 142.50, "currency": "AED",
])import SwanSDK from '@loyalytics/swan-react-native-sdk';
const sdk = SwanSDK.getCurrentInstance();
sdk?.productViewed({
productId: 'SKU-100',
productName: 'Cotton tee',
price: 49.95,
currency: 'AED',
category: 'Apparel/Tops',
});
sdk?.checkoutStarted({
checkoutId: 'co-abc',
orderId: 'ORD-9001',
totalAmount: 142.50,
productIds: [
{ productId: 'SKU-100', quantity: 1, price: 49.95 },
{ productId: 'SKU-200', quantity: 2, price: 46.275 },
],
});
sdk?.checkoutCompleted({
checkoutId: 'co-abc',
orderId: 'ORD-9001',
totalAmount: 142.50,
productIds: [
{ productId: 'SKU-100', quantity: 1, price: 49.95 },
{ productId: 'SKU-200', quantity: 2, price: 46.275 },
],
});The full set of typed methods — productViewed, productClicked,
productListViewed, productAddedToAddTocart, productRemovedFromAddToCart,
productAddedToWishlist, productRemovedFromWishlist,
wishlistProductAddedToCart, clearCart, cartViewed, selectCategory,
categoryViewedPage, offerAvailed, checkoutStarted, checkoutCompleted,
checkoutCanceled, paymentInfoEntered, orderCompleted, orderRefunded,
orderCancelled, orderExperianceRating, productRatedOrReviewed,
productReview, productQuantitySelected, purchased, shipped,
appLaunched, appUpdated, accountDeletion, forgotPassword,
search, share, screen — is exported as methods on the SDK instance.
Swan.SwanEvents.productViewed({
id: 'SKU-100', name: 'Cotton tee',
price: 49.95, currency: 'AED',
});Full catalog
| Method | Wire event name | Typical attributes |
|---|---|---|
appLaunched | appLaunched | {} (auto-tracked at SDK init too) |
appUpdated | appUpdated | { fromVersion, toVersion } |
accountDeletion | accountDeletion | {} |
forgotPassword | forgotPassword | { method } (email/sms) |
search | search | { searchKeyword } |
share | share | { productId, channel } |
productViewed | productViewed | { id, name, price, currency, category } |
productClicked | productClicked | { id, name, position, listName } |
productListViewed | productListViewed | { listName, listId, products[] } |
productRatedOrReviewed | productRatedOrReviewed | { id, rating, reviewBody? } |
productReview | productReview | { id, rating, reviewBody } |
productQuantitySelected | productQuantitySelected | { id, qty } |
productAddedToAddTocart | productAddedToaddTocart | { id, qty, price } |
productRemovedFromAddToCart | productRemovedFromAddToCart | { id, qty } |
clearCart | clearCart | {} |
cartViewed | cartViewed | { cartId, itemCount, totalValue } |
selectCategory | selectCategory | { id, name } |
categoryViewedPage | categoryViewedPage | { id, name, productCount } |
productAddedToWishlist | productAddedToWishlist | { id } |
productRemovedFromWishlist | productRemovedFromWishlist | { id } |
wishlistProductAddedToCart | wishlistProductAddedToCart | { id } |
offerAvailed | offerAvailed | { offerId, discount } |
checkoutStarted | checkoutStarted | { cartId, itemCount, totalValue, currency } |
checkoutCompleted | checkoutCompleted | { orderId, totalValue, currency } |
checkoutCanceled | checkoutCanceled | { cartId, reason } |
paymentInfoEntered | paymentInfoEntered | { method } |
orderCompleted | orderCompleted | { orderId, totalValue, currency, items[] } |
orderRefunded | orderRefunded | { orderId, amount, reason } |
orderCancelled | orderCancelled | { orderId, reason } |
orderExperianceRating | orderExperianceRating | { orderId, rating } |
purchased | purchased | { orderId, totalValue, currency } |
shipped | shipped | { orderId, trackingNumber } |
Wire names with unusual spellings
Two wire event names use spellings that look like typos but are
intentional: productAddedToaddTocart (lowercase a in the middle) and
orderExperianceRating (Experiance, not Experience). The SDK method
names are spelled normally (productAddedToAddTocart,
orderExperianceRating) so your call sites stay clean, but campaign
rules must match the wire spelling exactly.
3. Custom events — when typed don't fit
Anything domain-specific that isn't in the e-commerce catalog. The campaigns engine still ingests custom events; they just aren't auto-recognized as e-commerce.
Swan.customEvent("clickedHero", mapOf(
"variant" to "A",
"position" to 0,
))
// No attributes
Swan.customEvent("onboardingDismissed")Swan.shared.track("clickedHero", attributes: ["variant": "A", "position": 0])iOS routes custom events through track(_:attributes:) — the same wire
shape as Android's customEvent. No separate customEvent method.
import SwanSDK from '@loyalytics/swan-react-native-sdk';
const sdk = SwanSDK.getCurrentInstance();
sdk?.customEvent('clickedHero', { variant: 'A', position: 0 });
sdk?.customEvent('onboardingDismissed');Swan.customEvent('clickedHero', { variant: 'A', position: 0 });Naming convention: camelCase like the typed events (clickedHero,
onboardingDismissed). Keep names stable across versions — campaign rules
match on string equality.
4. Super-properties
Attributes that should ride along on every subsequent event, set once.
| Helper | Set-once values |
|---|---|
setCountry(string) | "AE", "SA", "US" — ISO-3166 alpha-2 recommended |
setCurrency(string) | "AED", "SAR", "USD" — ISO-4217 |
setBusinessUnit(string) | host-app-defined (e.g. "loyalty-app", "guest-checkout") |
setCurrentScreenName(string) | rides every event, refresh on each navigation |
Swan.setCountry("AE")
Swan.setCurrency("AED")
Swan.setBusinessUnit("loyalty-app")
// in your nav listener
Swan.setCurrentScreenName(currentRoute)Swan.shared.setCountry("AE")
Swan.shared.setCurrency("AED")
Swan.shared.setBusinessUnit("loyalty-app")
Swan.shared.setCurrentScreenName(currentRoute)import SwanSDK from '@loyalytics/swan-react-native-sdk';
const sdk = SwanSDK.getCurrentInstance();
sdk?.setCountry('AE');
sdk?.setCurrency('AED');
sdk?.setBusinessUnit('loyalty-app');
// in a navigation listener
navigation.addListener('state', () => {
const route = navigation.getCurrentRoute()?.name;
if (route) sdk?.setCurrentScreenName(route);
});Swan.setCountry('AE');
Swan.setCurrency('AED');
Swan.setBusinessUnit('loyalty-app');Super-properties are per-instance, in-memory. They reset on app restart — set them in your app's init code so they're always present.
5. Manual flush
The SDK flushes on its own schedule (every ~30s, or once 10 events are queued — the default batch size — or on app foreground / background transitions). You rarely need to flush manually. Cases where you might:
- Just before logout / sign-out — ensure the last anonymous events ingest
under the right CDID before the rotation. (Note:
login(...)already flushes for you; you only need explicit flush aroundlogout()for an extra guarantee.) - In an integration test — to assert the wire round-trip without waiting for the timer.
- Before exiting the app on a controlled path — e.g. a custom "exit" button — the lifecycle observer already covers normal backgrounding.
Swan.flush()Swan.shared.flush()import SwanSDK from '@loyalytics/swan-react-native-sdk';
const sdk = SwanSDK.getCurrentInstance();
await sdk?.flushEvents();await Swan.flush();6. Inspect queue + session
For diagnostics — verify the SDK is queueing correctly, or correlate a user session with a downstream event in your warehouse.
val queued: Int = Swan.getQueueSize()
val session: String? = Swan.getCurrentSessionId()
val info = Swan.getDeviceInfo()
// info.identifier, info.generatedCDID, info.currentCDID, ...let queued = Swan.shared.getQueueSize()
let session = Swan.shared.getCurrentSessionId()
let info = Swan.shared.getDeviceInfo()import SwanSDK from '@loyalytics/swan-react-native-sdk';
const sdk = SwanSDK.getCurrentInstance();
const queued = await sdk?.getQueueSize();
const info = await sdk?.getDeviceInfo();
// info: { deviceId, generatedCDID, currentCDID, identifier?, subscribedAt?, ... }
const swanId = await sdk?.getSwanIdentifier();
// swanId = info.currentCDID (logged in) ?? info.generatedCDID (anonymous)getSwanIdentifier() returns the SDK's current identifier — the
currentCDID when identified, otherwise the anonymous generatedCDID.
Returns undefined only if device registration hasn't completed yet.
const queued = await Swan.getQueueSize();
const info = await Swan.getDeviceInfo();Reference
- Concepts → Event model — sessions, flush, batching, retry
- Identify users — make sure events attribute to the right profile
- Android:
SwanEvents,Swan.track,Swan.customEvent