Skip to main content

Custom Events

Generates: Events (Log Records)

Track business-level interactions, feature usage, and user milestones with named events and arbitrary attributes.

Already using an analytics service?

If you're using Mixpanel, Amplitude, Firebase Analytics, Segment, or similar — you can bridge those calls to Pulse.shared.trackEvent(...) alongside your existing tracking. This means zero additional instrumentation effort: every business event you already track automatically flows into Pulse too.

These events are the raw signal that Pulse uses to derive Interactions — user journey flows that are automatically composed, scored with Apdex, and surfaced in the dashboard to show what matters to your users.

Basic Usage

Pulse.shared.trackEvent(
name: "purchase_completed",
observedTimeStampInMs: Int64(Date().timeIntervalSince1970 * 1000),
params: [
"item.id": "sku_123",
"item.name": "Premium Plan",
"order.total": 49.99,
"currency": "USD",
]
)

Parameters

ParameterTypeRequiredDescription
nameStringYesEvent name. Use snake_case or dot.notation consistently across your app
observedTimeStampInMsInt64YesEvent timestamp in epoch milliseconds
params[String: Any?]NoArbitrary attributes (string, number, bool, or arrays of each)

Timestamp Helper

let now = Int64(Date().timeIntervalSince1970 * 1000)

Common Event Patterns

Feature Usage

Pulse.shared.trackEvent(
name: "feature.used",
observedTimeStampInMs: Int64(Date().timeIntervalSince1970 * 1000),
params: [
"feature.name": "dark_mode",
"feature.enabled": true,
]
)

Onboarding Steps

Pulse.shared.trackEvent(
name: "onboarding.step_completed",
observedTimeStampInMs: Int64(Date().timeIntervalSince1970 * 1000),
params: [
"step.index": 2,
"step.name": "profile_setup",
]
)
Pulse.shared.trackEvent(
name: "search.performed",
observedTimeStampInMs: Int64(Date().timeIntervalSince1970 * 1000),
params: [
"search.query": query,
"search.result_count": results.count,
"search.filter": activeFilter,
]
)

E-commerce

// Add to cart
Pulse.shared.trackEvent(
name: "cart.item_added",
observedTimeStampInMs: Int64(Date().timeIntervalSince1970 * 1000),
params: [
"item.id": product.id,
"item.name": product.name,
"item.price": product.price,
"item.category": product.category,
]
)

// Checkout completed
Pulse.shared.trackEvent(
name: "checkout.completed",
observedTimeStampInMs: Int64(Date().timeIntervalSince1970 * 1000),
params: [
"order.id": orderId,
"order.total": total,
"order.currency": "USD",
"payment.method": "apple_pay",
]
)

Generated Telemetry

Type: Log Record (Event)
Body: Event name
pulse.type: custom_event

Attributes

AttributeDescriptionAlways Present
pulse.typeAlways "custom_event"✅ Yes
event.nameThe event name you provided✅ Yes
session.idCurrent session identifier✅ Yes
screen.nameActive UIViewController⚠️ If available
(your params)All key/value pairs in params⚠️ As provided

Sample Payload

{
"body": "purchase_completed",
"attributes": {
"pulse.type": "custom_event",
"event.name": "purchase_completed",
"item.id": "sku_123",
"item.name": "Premium Plan",
"order.total": 49.99,
"currency": "USD",
"session.id": "f40364c92b85ec0c19c35a65be42b97f",
"screen.name": "CheckoutViewController"
}
}

Best Practices

  1. Use consistent naming: Pick a convention (snake_case or dot.notation) and apply it everywhere. Event names are case-sensitive in the Pulse dashboard.
  2. Keep names semantic: purchase_completed is better than button_tapped. Name the business action, not the UI interaction.
  3. Avoid high-cardinality keys: Don't put raw user-generated text (search queries, user names) directly into attribute keys — use values instead.
  4. Avoid null values: Omit keys whose values are not known rather than setting them to nil.