> ## Documentation Index
> Fetch the complete documentation index at: https://docs.mutalabs.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Analytics Integration

> Learn how to integrate analytics with your Muta placements

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:

```swift theme={null}
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.

```swift theme={null}
case flowStarted(FlowStartedEvent)
// Properties: placementId, flowName?, totalScreens, timestamp
```

### 2. Screen Viewed (`screenViewed`)

Emitted when a user views a new screen in the flow.

```swift theme={null}
case screenViewed(ScreenViewedEvent)
// Properties: placementId, flowName?, screenIndex, totalScreens, screenName?, timestamp
```

### 3. Flow Completed (`flowCompleted`)

Emitted when a user successfully finishes the flow.

```swift theme={null}
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:**

```swift theme={null}
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.

```swift theme={null}
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.

```swift theme={null}
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

```swift theme={null}
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.

```swift theme={null}
case error(ErrorEvent)
// Types: .network(message, timestamp) or .placement(message, code, timestamp, placementId)
```

## Integration Examples

### Mixpanel Example

```swift theme={null}
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

```swift theme={null}
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.
