Skip to main content
Muta provides a flexible event system that works with any analytics provider (Mixpanel, AppsFlyer, Amplitude, Firebase, etc.). Events are emitted during key user interactions, allowing you to track and analyze your placement performance.

Basic Integration

Here’s a simple example of how to integrate analytics with your Muta placements:
import MutaSDK

class AnalyticsManager {
    var subscription: Subscription?
    
    func setupTracking() {
        subscription = Muta.shared.on { event in
            switch event {
            case .flowStarted(let event):
                Analytics.track("Flow Started", properties: [
                    "placement_id": event.placementId,
                    "flow_name": event.flowName ?? "",
                    "total_screens": event.totalScreens,
                    "timestamp": event.timestamp
                ])
                
            case .flowCompleted(let event):
                var properties: [String: Any] = [
                    "placement_id": event.placementId,
                    "flow_name": event.flowName,
                    "screen_index": event.screenIndex,
                    "total_screens": event.totalScreens,
                    "timestamp": event.timestamp
                ]

                // Include collected variables if present
                if let variables = event.eventData?["variables"] as? [String: Any] {
                    properties["variables"] = variables
                }

                Analytics.track("Flow Completed", properties: properties)
                
            case .custom(let event):
                Analytics.track(event.type, properties: [
                    "placement_id": event.placementId,
                    "flow_name": event.flowName ?? "",
                    "event_data": event.eventData ?? [:],
                    "timestamp": event.timestamp
                ])
                
            // Handle other events...
            }
        }
    }
    
    deinit {
        subscription?.unsubscribe()
    }
}

Available Events

1. Flow Started (flowStarted)

Emitted when a placement flow begins displaying.
case flowStarted(FlowStartedEvent)
// Properties: placementId, flowName?, totalScreens, timestamp

2. Screen Viewed (screenViewed)

Emitted when a user views a new screen in the flow.
case screenViewed(ScreenViewedEvent)
// Properties: placementId, flowName?, screenIndex, totalScreens, screenName?, timestamp

3. Flow Completed (flowCompleted)

Emitted when a user successfully finishes the flow.
case flowCompleted(FlowCompletedEvent)
// Properties: placementId, flowName, screenIndex, totalScreens, screenName, timestamp, eventData?
// eventData contains variables collected during the flow

Variable Structure

When a flow completes, eventData["variables"] contains a dictionary where:
  • Key: Variable ID (e.g., var_1758310510757_as20wkeo4)
  • Value: Dictionary containing:
    • id (String): Unique variable identifier
    • name (String): Human-readable variable name from the Muta editor
    • type (String): Variable type (e.g., “text”)
    • value (Any): The collected value
    • defaultValue (Any): The default value
Example:
case .flowCompleted(let event):
    if let variables = event.eventData?["variables"] as? [String: [String: Any]] {
        for (varId, varData) in variables {
            let name = varData["name"] as? String ?? ""
            let value = varData["value"] as? String ?? ""
            print("Variable '\(name)': \(value)")
        }
    }
Output:
Variable 'MultipleChoice1': Family
Variable 'MultipleChoice2': Fluent

4. Flow Abandoned (flowAbandoned)

Emitted when a user exits the flow before completion.
case flowAbandoned(FlowAbandonedEvent)
// Properties: placementId, flowName?, screenIndex, totalScreens, lastScreenIndex, screenName?, timestamp

5. Custom Events

Emitted when users trigger actions configured with “Emit Event” behavior in the Muta web editor.
case custom(CustomEvent)
// Properties: type, placementId, flowName?, screenIndex?, timestamp, eventData?

Setting Up Custom Events

  1. In the Muta web editor, add an “Emit Event” behavior to any element
  2. Give your event a unique name (e.g., signup_started, premium_selected)
  3. Optionally add custom data to pass along with the event

Listening for Custom Events

subscription = Muta.shared.on { event in
    if case .custom(let customEvent) = event {
        switch customEvent.type {
        case "signup_started":
            // Handle signup
            navigateToSignup()
            
        case "plan_selected":
            if let planType = customEvent.eventData?["planType"] as? String {
                selectPlan(planType)
            }
            
        default:
            // Track any custom event
            analytics.track(customEvent.type, properties: customEvent.eventData ?? [:])
        }
    }
}

6. Error Events (error)

Emitted when there are network or placement errors.
case error(ErrorEvent)
// Types: .network(message, timestamp) or .placement(message, code, timestamp, placementId)

Integration Examples

Mixpanel Example

import MutaSDK
import Mixpanel

class MixpanelAnalytics {
    var subscription: Subscription?
    
    func setupTracking() {
        subscription = Muta.shared.on { event in
            switch event {
            case .flowCompleted(let event):
                Mixpanel.mainInstance().track(
                    event: "Onboarding Complete",
                    properties: [
                        "flow_name": event.flowName,
                        "total_screens": event.totalScreens
                    ]
                )
                
            case .flowAbandoned(let event):
                Mixpanel.mainInstance().track(
                    event: "Onboarding Abandoned",
                    properties: [
                        "flow_name": event.flowName ?? "",
                        "last_screen_index": event.lastScreenIndex,
                        "total_screens": event.totalScreens
                    ]
                )
                
            case .custom(let event):
                // Track custom events
                Mixpanel.mainInstance().track(
                    event: event.type,
                    properties: event.eventData ?? [:]
                )
                
                // Example: Track form submission
                if event.type == "form_submitted" {
                    if let formData = event.eventData {
                        Mixpanel.mainInstance().track(
                            event: "Form Submitted",
                            properties: formData
                        )
                    }
                }
                
            default:
                break
            }
        }
    }
    
    deinit {
        subscription?.unsubscribe()
    }
}

AppsFlyer Example

import MutaSDK
import AppsFlyerLib

class AppsFlyerAnalytics {
    var subscription: Subscription?
    
    func setupTracking() {
        subscription = Muta.shared.on { event in
            var eventName = ""
            var properties: [String: Any] = [:]
            
            switch event {
            case .flowStarted(let e):
                eventName = "onboarding_flow_started"
                properties = [
                    "placement_id": e.placementId,
                    "flow_name": e.flowName ?? "",
                    "total_screens": e.totalScreens,
                    "timestamp": e.timestamp
                ]
                
            case .screenViewed(let e):
                eventName = "onboarding_screen_viewed"
                properties = [
                    "placement_id": e.placementId,
                    "flow_name": e.flowName ?? "",
                    "screen_index": e.screenIndex,
                    "total_screens": e.totalScreens,
                    "screen_name": e.screenName ?? ""
                ]
                
            case .flowCompleted(let e):
                eventName = "onboarding_flow_completed"
                properties = [
                    "placement_id": e.placementId,
                    "flow_name": e.flowName,
                    "screen_index": e.screenIndex,
                    "total_screens": e.totalScreens,
                    "screen_name": e.screenName
                ]
                
            case .flowAbandoned(let e):
                eventName = "onboarding_flow_abandoned"
                properties = [
                    "placement_id": e.placementId,
                    "flow_name": e.flowName ?? "",
                    "screen_index": e.screenIndex,
                    "total_screens": e.totalScreens,
                    "last_screen_index": e.lastScreenIndex
                ]
                
            case .custom(let e):
                // Use the custom event type as the event name
                eventName = e.type
                properties = [
                    "placement_id": e.placementId,
                    "flow_name": e.flowName ?? "",
                    "screen_index": e.screenIndex ?? -1
                ]
                // Merge custom event data
                if let eventData = e.eventData {
                    properties.merge(eventData) { _, new in new }
                }
                
            case .error(let error):
                eventName = "onboarding_error"
                switch error {
                case .network(let message, _):
                    properties = [
                        "error_message": message,
                        "error_type": "network"
                    ]
                    
                case .placement(let message, let code, _, let placementId):
                    properties = [
                        "error_message": message,
                        "error_code": code,
                        "error_type": "placement",
                        "placement_id": placementId
                    ]
                @unknown default:
                    break
                }
            @unknown default:
                break
            }
            
            if !eventName.isEmpty {
                AppsFlyerLib.shared().logEvent(eventName, withValues: properties)
            }
        }
    }
    
    deinit {
        subscription?.unsubscribe()
    }
}

Best Practices

  1. Event Naming: Use consistent event naming conventions across your analytics platform.
  2. Data Enrichment: Add additional context to events when needed (e.g., user ID, app version).
  3. Error Handling: Implement proper error handling for analytics tracking.
  4. Custom Event Documentation: Document your custom events and their expected data structure.
  5. Performance: Consider batching events for better performance, especially for high-frequency events.
  6. Privacy: Ensure you’re not tracking sensitive information in your analytics events.