Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.permutive.com/llms.txt

Use this file to discover all available pages before exploring further.

While PageTracker is the recommended approach for most tracking, you can also track custom events directly using the track(event:properties:) method.

Basic Tracking

Schema Validation

When to Use Direct Event Tracking

Use direct event tracking for:
  • Events that don’t fit the page view model
  • Background events (app state changes, push notifications)
  • One-off events without context
  • Events outside of a view lifecycle
For view-based tracking with engagement time, scroll depth, and contextual cohorts, use PageTracker instead.

Tracking Events

Basic Event

do {
    try Permutive.shared.track(event: "AppLaunch")
} catch {
    print("Failed to track event: \(error)")
}

Event with Properties

do {
    let properties = try EventProperties([
        "button_name": "subscribe",
        "position": "header",
        "is_premium": true
    ])
    try Permutive.shared.track(event: "ButtonClick", properties: properties)
} catch {
    print("Failed to track event: \(error)")
}

Event Naming Rules

Event names must follow these rules:
  • Only alphanumeric characters and underscores: [a-zA-Z0-9_]
  • Cannot start with a number
  • Case-sensitive
"AppLaunch"           // ✅
"button_click"        // ✅
"VideoPlay2"          // ✅
"user_subscription"   // ✅

Schema Validation

Event properties must match exactly the schema defined in your Permutive dashboard. Mismatched properties cause event rejection.

Enable Debug Logging

options.logModes = LogMode.all

Schema Error Example

When properties don’t match the schema:
Permutive: [error] Encountered 1 event rejection!
Permutive: [error] Error 1: A validation check carried out by the server did not succeed.(1007)
Permutive: [error] Error 1: Schema validation failed with reason(s): [#: extraneous key [invalid_key] is not permitted]

Checking Events Were Accepted

Successful events show:
Permutive: [info] Accepted: 1 / 1

Common Event Types

App Lifecycle Events

// App launch
func applicationDidBecomeActive() {
    try? Permutive.shared.track(
        event: "AppActive",
        properties: try? EventProperties([
            "launch_type": isFirstLaunch ? "fresh" : "resume"
        ])
    )
}

// App background
func applicationWillResignActive() {
    try? Permutive.shared.track(event: "AppBackground")
}

User Action Events

// Button click
func onSubscribeButtonTapped() {
    try? Permutive.shared.track(
        event: "SubscribeClick",
        properties: try? EventProperties([
            "plan": "premium",
            "source": "paywall"
        ])
    )
}

// Search
func onSearch(query: String, resultCount: Int) {
    try? Permutive.shared.track(
        event: "Search",
        properties: try? EventProperties([
            "query_length": query.count,
            "result_count": resultCount,
            "has_results": resultCount > 0
        ])
    )
}

Push Notification Events

func userNotificationCenter(
    _ center: UNUserNotificationCenter,
    didReceive response: UNNotificationResponse
) {
    let userInfo = response.notification.request.content.userInfo

    try? Permutive.shared.track(
        event: "PushNotificationOpen",
        properties: try? EventProperties([
            "campaign_id": userInfo["campaign_id"] as? String ?? "unknown",
            "action": response.actionIdentifier
        ])
    )
}

Purchase Events

func onPurchaseComplete(product: SKProduct, transaction: SKPaymentTransaction) {
    try? Permutive.shared.track(
        event: "Purchase",
        properties: try? EventProperties([
            "product_id": product.productIdentifier,
            "price": product.price.doubleValue,
            "currency": product.priceLocale.currencyCode ?? "USD",
            "transaction_id": transaction.transactionIdentifier ?? ""
        ])
    )
}

Event Enrichment

Add server-side enrichment to events:
let properties = try EventProperties([
    "geo_info": EventProperties.geoInfoValue,
    "isp_info": EventProperties.ispInfoValue,
    "form_id": "contact_form"
])

try Permutive.shared.track(event: "FormSubmit", properties: properties)
See Event Properties for all enrichment options.

Batching and Delivery

Events are:
  • Queued locally when tracked
  • Sent in batches to reduce network overhead
  • Persisted to disk if the app closes before sending
  • Retried automatically on network failure
You don’t need to manage batching - it’s handled automatically.

Error Handling

func trackEvent(name: String, properties: [String: Any]) {
    do {
        let eventProperties = try EventProperties(properties)
        try Permutive.shared.track(event: name, properties: eventProperties)
    } catch let error as NSError {
        // Log but don't crash - tracking failures shouldn't break app
        print("Event tracking failed: \(error.localizedDescription)")

        // Optionally report to error tracking
        // Crashlytics.log("Permutive track error: \(error)")
    }
}

Best Practices

  • Use descriptive, consistent event names
  • Match your dashboard schema exactly
  • Test events with debug logging enabled
  • Handle errors gracefully
  • Use PageTracker for view-based tracking
  • Keep properties minimal and relevant

Troubleshooting

Problem: Tracked events don’t show up.Solutions:
  • Enable debug logging and look for Accepted: X / X
  • Check for schema validation errors in logs
  • Wait up to 5 minutes for processing
  • Verify you’re in the correct workspace
Problem: Creating EventProperties throws an error.Solutions:
  • Verify all values are supported types
  • Check for unsupported custom objects
  • Ensure arrays contain only supported types
Problem: Events sent but rejected by server.Solutions:
  • Check event name matches schema exactly
  • Verify property names and types
  • Remove properties not in the schema
  • Check for typos

Page Tracking

View-based tracking with PageTracker

Event Properties

Structuring event metadata

Video Tracking

Track video content

Issues

Common problems and solutions