--- title: Track time-based user engagement with Duration Events tags: - setup - how-to - beginner - swift description: Duration Events allow you to measure how long users spend on specific activities in your app with millisecond precision. lead: With Duration Events, you can easily measure how long users spend on different activities in your app, helping you identify engagement patterns, optimize workflows, and improve user experience with precise timing data. --- ## What are Duration Events? Duration Events are a powerful feature of TelemetryDeck's SDKs that make it easier than ever to understand how users interact with your app over time. Whether you want to track time spent during onboarding, content consumption, checkout flows, or any other user journey, Duration Events provide accurate, millisecond-precise timing data. The SDK automatically handles all the complexities of time tracking for you: - Precise measurement down to milliseconds (3 decimal places) - Automatic exclusion of time spent while the app is in the background - Merging parameters from both start and stop calls - Thread-safe implementation for accurate timing ## Implementation Using Duration Events is as simple as bracketing an activity with two function calls: ```swift // Start tracking when the activity begins TelemetryDeck.startDurationSignal("activityName") // ... user performs the activity ... // Stop tracking and send the event when the activity ends TelemetryDeck.stopAndSendDurationSignal("activityName") ``` The duration is automatically calculated and included in your event as `TelemetryDeck.Signal.durationInSeconds`. ### View Lifecycle Integration Duration Events integrate seamlessly with your view lifecycles in SwiftUI: ```swift struct TutorialView: View { var body: some View { VStack { Text("Welcome to the Tutorial!") } .onAppear { TelemetryDeck.startDurationSignal("tutorial") } .onDisappear { TelemetryDeck.stopAndSendDurationSignal("tutorial") } } } ``` Both functions also take an optional `parameters` argument where you can pass additional information just like with the `signal` function. ## Technical Details ### SDK Requirements - Swift SDK: Version 2.7.0 or later - Kotlin SDK: Version 4.1.0 or later - Flutter SDK: Version 2.1.0 or later ### Edge Cases & Limitations - **Multiple starts**: If you call `startDurationSignal` with a name that's already being tracked, the previous tracking is discarded and a new one begins. - **Missing stop**: If a duration event is never stopped, it will not be sent. - **Event name conflicts**: Use unique event names for different activities to avoid conflicts. - **App restarts**: Duration Events are not stored to persistent storage, therefore they are not appropriate for tracking long-term user engagement. ## Analyzing Duration Data Duration data is sent as a numerical value in the `TelemetryDeck.Signal.durationInSeconds` parameter, which opens up several analysis possibilities. ### Using the Histogram Aggregation The histogram aggregation type is perfect for visualizing the distribution of duration data: 1. Create a new insight of type "Advanced Query", then open the "JSON Editor": ![A screenshot of the Query Creator dialog](/docs/images/duration-signal-01.png) 2. Copy & paste the following histogram aggregation query and adjust `` to your needs: ![A screenshot of the JSON Editor text field](/docs/images/duration-signal-02.png) ```json { "aggregations": [ { "fieldName": "TelemetryDeck.Signal.durationInSeconds", "name": "durationSketch", "splitPoints": [0, 0.5, 1, 1.5, 2, 2.5, 3, 4, 5, 7.5, 10, 15, 20, 30, 45, 60, 90, 120], "type": "histogram" } ], "filter": { "type": "and", "fields": [ { "type": "range", "column": "TelemetryDeck.Signal.durationInSeconds", "matchValueType": "DOUBLE", "lower": "0", "upper": "120", "upperOpen": true }, { "dimension": "type", "type": "selector", "value": "" } ] }, "granularity": "all", "queryType": "timeseries" } ``` 3. Set the chart type to be a bar chart in the UI via the insight's top right segmented control: ![A screenshot of the insight set to be a bar chart](/docs/images/duration-signal-03.png) 4. You might also want to adjust the `splitPoints` array based on the expected duration of your activity, for example: - **Short interactions** (button clicks): `[0, 0.05, 0.1, 0.15, 0.2, 0.3, 0.5, 1, 2, 5]` - **Medium interactions** (form fills): `[0, 1, 2, 3, 4, 5, 7.5, 10, 15, 20, 30]` - **Long interactions** (content consumption): `[0, 5, 15, 30, 60, 120, 300, 600, 1200]` ## Common Use Cases ### Onboarding Optimization Track time spent in each step of your onboarding flow to identify which steps take too long or where users might get stuck: ```swift // In first onboarding screen TelemetryDeck.startDurationSignal("Onboarding.step1") // When moving to second screen TelemetryDeck.stopAndSendDurationSignal("Onboarding.step1", parameters: ["pushAccess": "granted"]) TelemetryDeck.startDurationSignal("Onboarding.step2") // etc. ``` Note that Duration Events are just ordinary events, so you can totally reuse these for creating [funnel charts](https://telemetrydeck.com/docs/articles/how-to-funnel-insights/) and more. ### Content Engagement Measure how long users engage with different content types to understand what resonates with your audience: ```swift // When user opens an article TelemetryDeck.startDurationSignal("Content.viewing", parameters: [ "contentType": "article", "contentID": article.id, "contentCategory": article.category, ]) // When user leaves the article TelemetryDeck.stopAndSendDurationSignal("Content.viewing", parameters: [ "reachedEnd": userReachedEnd ? "true" : "false", ]) ``` ### Feature Discovery Track how long users spend exploring new features to assess the effectiveness of your feature introduction: ```swift // When user enters new feature area TelemetryDeck.startDurationSignal("Feature.exploration", parameters: [ "featureName": "videoEditor", "entryPoint": entryPoint, ]) // When user leaves the feature area TelemetryDeck.stopAndSendDurationSignal("Feature.exploration", parameters: [ "completedAction": userCreatedVideo ? "true" : "false" ]) ``` ### Performance Monitoring Track real-world performance metrics by measuring operation durations: ```swift // Before starting an intensive operation TelemetryDeck.startDurationSignal("Render.operation", parameters: [ "complexity": "\(complexity)", "inputSize": "\(inputSizeInMB)", ]) // After operation completes TelemetryDeck.stopAndSendDurationSignal("Render.operation", parameters: [ "success": success ? "true" : "false", "outputSize": "\(outputSizeInMB)", ]) ``` ### Network Request Timing Duration Events methods are marked with `@MainActor`, which means two things: 1. In UI contexts like SwiftUI views, no `await` is needed (as shown in the above examples) 2. When calling from background contexts like network operations, you need to use `await` Here's how to measure network requests that run on background threads: ```swift func fetchData() async throws -> Data { // Since we're potentially on a background thread, await is needed await TelemetryDeck.startDurationSignal("Network.fetch", parameters: [ "endpoint": "users/profile" ]) do { // Perform your network request let (data, response) = try await URLSession.shared.data(from: url) let statusCode = (response as? HTTPURLResponse)?.statusCode ?? 0 // Again, await is needed here await TelemetryDeck.stopAndSendDurationSignal("Network.fetch", parameters: [ "status": "\(statusCode)", "success": "true" ]) return data } catch { await TelemetryDeck.stopAndSendDurationSignal("Network.fetch", parameters: [ "success": "false" ]) throw error } } ``` ## What's Next? Start by identifying a few key user journeys or critical performance areas in your app that would benefit from timing data. Implement Duration Events for these activities first, then use the histogram aggregation to visualize and analyze the results. Remember that Duration Events can be combined with your existing analytics strategy - they provide an additional dimension to your user data without replacing what you already have. {% callToAction "Explore more analytics possibilities" "Track user engagement and make data-driven decisions" %}