---
title: Data Collection - iOS
description: Understand how the iOS SDK loads, tracks, and send data
lastUpdated: 14 January 2026
source_url:
  html: https://docs.contentsquare.com/en/csq-sdk-ios/experience-analytics/data-collection/
  md: https://docs.contentsquare.com/en/csq-sdk-ios/experience-analytics/data-collection/index.md
---

## Initialization

The way our SDK works is by auto-starting with the application launch and attaching to the current process in order to intercept the events and gestures we are interested in.

See [Session Replay Initialization](../session-replay/#initialization) to learn how Session Replay is initialized.

## Configuration

Once started, our SDK fetches it configuration from our servers. Then depending on the segmentation size (defined by our team when you sign a contract) and the user consent status, it will start collecting analytics data from system and user events it detects from the runtime.

## Data collection

### Analytics data collection

The SDK monitors the application, lifecycle events, the view hierarchy, and generates data from the behavior of the app, the content of the screen and the user interactions. These events are then locally stored, and eventually sent to our servers in batches. We then aggregate that data to create usable visual information into our Web Application, which you use to gather insights.

### Session Replay data collection

See [Session Replay Initialization](../session-replay/#data-collection) to learn how Session Replay data collection works.

## Sending data

### Sending analytics data

Analytics data are sent in batches of maximum 50 events. Requests are triggered when network conditions allow for the server to be reached and:

* The current batch of events has reached 50
* Or the app is put in background

### Sending Session Replay Data

See [Session Replay Requests](../session-replay/#requests) to learn more about how Session Replay data is sent.

### Sending crash Data

See [Send Crash data](../error-analysis/#sending-crash-data) to learn more about how crash data is sent.

## Session definition

A session represents a single period of user interaction in the app. In Contentsquare, the SDK marks the start of a session when an `app start` or `app show` event is first detected.

A session ends **30 minutes** after the last event. This is the default value which be changed upon request.

If the app is put in the background or killed (intentionally by the user or by the OS) and the user comes back within the next 30 minutes, subsequent events are considered part of the same session.

## Collected data points

* The events are in JSON format, which lists the properties and their description in comment.
* Events are sent to the server in batches.
* A batch is a subset of events originating from one session. A session can send events in more than one batch.
* The batch size is defined in a run configuration file specific to each app (50 events by default).

### Requests meta data

Each batch has a common header, a set of device specific, and repeatable information and an array of events, which is immutable. The header is composed at the moment of sending, so it has to be constant info ONLY. The array in the payload is data stored at the moment of collection.

The structure of the batches of events will have the following format:

```javascript
{
  pid:8, // int - ProjectId - represented in the mobile config format
  uid:"ac1529a7-59f6-49d9-b254-6f0a22f57284", // String - uuid Unique user ID
  dt:4, // int - device type (loosely enforced enum - [sdk-phone : 4, sdk-tablet :5])
  dma:"Samsung", // String - device manufacturer. This property is optional and if by any chance the device model cannot be determined it will be dropped from the Json object.
  dmo:"SG", // String - device model. This property is optional and if by any chance the device model cannot be determined it will be dropped from the Json object.
  os:"10.0", // String - os version (Android version name 8.0, 9.0, 10.0)
  l:"en_US", // String - Language in iso-639 format https://www.ibabbleon.com/iOS-Language-Codes-ISO-639.html
  tz:"Europe/Paris", // String - timezone https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
  now:1522159618000, // long - timestamp when the batch was sent
  to:{ // object - type origin of the event
    an:"appname" // String - application name
    st:"sdk-android", // String - type of sdk
    sf:"release", // string - sdk flavor/variant [release/debug/god]
  },
  r:{ // Device - resolution.
    w:1080, // int - width
    h:1920 // int - height
    d: 1.5 // The device density as float. Its values can vary between (0.75 and 10)
  },
  pl:[] // JSON array payload - list of event objects, see below
}
```

### Events meta data

All events have a common first part (don't confuse it with the header explained above). This common section is data which is common for all events by type but is gathered at the moment the event occurred. This common section is a part of the actual event payload.

```javascript
{
  upt:1522, // long - system update in milliseconds
  euid:"euid", // String - event UUID
  url:"app-and://identifier/mainPath/secondaryPath?title=screeName", // String - screenName is passed in the title query Parameter
  scn:4, // int - the number of screens already shown in the session
  c:3, // int - connectivity type [-1 - offline, 0 - error, 1 - wifi, 2 - 2g, 3 - 3g, 4 - 4g]
  ci:"verizon", // String - carrier_id when user is on mobile network
  o:1, // int - orientation [0 = portrait, 1 = landscape]
  vo:{ // object - version origin of the event
    sv:"2.1", // string version of the sdk
    sb:4, // int - sdk build number
    av:"appVersion", // String - application version
    af:"appFlavor" // String - application string - [release/debug/god]
    ab:1 // int - application build number
  },
  sn:1, // int - session id (positive int)
  t:12894726459435 // long - timestamp of the event (in milliseconds)
}
```

All event specific properties are appended to this JSON object.

### App Start

This event describes the absolute start of the app.

The trigger for this event is the absolute start of the app.

This is the first event sent when the SDK is invoked.

```javascript
ea: 0; // int - event action - defined above
```

### App Show

This event is sent when the user brings the app in the foreground (switching from another app, exiting lock screen, etc.). This event means the app is focused.

```javascript
ea: 1; // int - event action - defined above
```

### App Hide

This event is sent when the user exits (minimizes) the app, or switches to something else. This event means the app is not focused and the session might end, but we have not completely ended the session yet, as the user might return.

```javascript
ea: 2; // int - event action - defined above
```

### Screenview

Everything starts with a View event. This is an event which describes the equivalent of a "page view" on the web. This event is sent when the Track Screen API is called.

```javascript
ea:4 // int - event action - defined above
st:"title", // String - screen title
sl:34543 // int - (load time) screen load time (diff between last action and this event)
```

### Tap

Single Finger gesture event, when the user is interacting with a loaded screen. This is an event which describes the equivalent of a "click" in the web. This event is defined by the following sequence of touch events:

* Touch Down -> N x Touch Move -> Touch Up

```javascript
ea:6 // int - event action - defined above
tvp:"[root]>view#id>view2>", // String - target view path
tvid:"btn_ok", // String - target view id
ur: true, // boolean - was this a "responsive" touch event (the target view had a handler)
```

### Long press

Single Finger gesture event, when the user is interacting with a loaded screen.

This event is defined by the following sequence of touch events:

* Touch Down → N x Touch Move → Touch Up
* Duration: > 500ms
* Distance: < 24 dp

```javascript
ea:8 // int - event action - defined above
tvp:"[root]>view#id>view2>", // String - target view path
tvid:"btn_ok", // String - target view id
```

### Drag (Slow Swipe)

Single Finger gesture event, when the user is interacting with a loaded screen.

This event is defined by the following sequence of touch events:

* Touch Down → N x Touch Move → Touch Up
* Distance: > 48 dp
* Finger Velocity < 100 dp/s

```javascript
ea:9 // int - event action - defined above
tvp:"[root]>view#id>view2>", // String - target view path
tvid:"btn_ok", // String - target view id
fd: 3, // int - finger direction - [1,2,3,4,5] -> [up, down, left, right, complex_pattern]
tvd:100, // int - target view distance dragged
tvv:100 // int - target view velocity while dragging dp/s
```

### Flick (Fast Swipe)

Single Finger gesture event, when the user is interacting with a loaded screen.

This event is defined by the following sequence of touch events:

* Touch Down → N x Touch Move → Touch Up
* Distance: > 48 dp
* Finger Velocity > 100 dp/s

```javascript
ea:10 // int - event action - defined above
tvp:"[root]>view#id>view2>", // String - target view graph path
tvid:"btn_ok", // String - target view id
fd: 3, // int - finger direction - [1,2,3,4,5] -> [up, down, left, right, complex_pattern]
tvd:100, // int - target view distance scrolled
tvv:100 // int - target view velocity while scrolling dp/s
```

### Transaction

To track transactions we provide a public API which can send a transaction object (see section [Track Transactions](../track-transactions/)). This object must contain the following parameters:

```javascript
ea:16, // int - event action - defined above
tr:{ // a json object with different properties defining the transaction made
    vl: 20.0, // mandatory - float - the value of the transaction
    cu: 978, // mandatory - int - the ISO 4217 code of the currency of the transaction
    id: "32akjkljklJ575775" // optional - string - an id for the transaction
}
```

### Dynamic variables

To track dynamic variables we provide a public API (see section [Dynamic variables](../track-dynamic-variables/)).

```javascript
ea:18, // int - event action - defined above
k:"key", // String - Custom key assigned by client.
v:"value" // String - Custom value assigned by client.
```

```javascript
ea:19, // int - event action - defined above
k:"key", // String - Custom key assigned by client.
v: 2344 // Integer - Custom value assigned by client.
```

## Reliable targets

Limitations

* For now, reliable targets are available for UIKit only.
* If the view doesn't qualify to be a reliable target then we use our current mechanism for zoning
* Reliable identifier must be unique per screen

In the Zoning Analysis module, reliable targets are assigned to zones to ensure that a `UIView` remains accessible in the view hierarchy, even if it has been moved within the same page.\
Having a reliable target for a view or a container of views enables the analysis of data across various Screenshots from different dates and versions, including layouts or A/B testing variations.

One of the following properties must be assigned for a view to have a reliable target.\
Our SDK will check the properties in this order:

1. [`accessibilityIdentifier` ↗](https://developer.apple.com/documentation/uikit/uiaccessibilityidentification/1623132-accessibilityidentifier)
2. [`restorationIdentifier` ↗](https://developer.apple.com/documentation/uikit/uiviewcontroller/1621499-restorationidentifier)
3. [`tag` ↗](https://developer.apple.com/documentation/uikit/uiview/1622493-tag)

### accessibility Identifier

if the view has `accessibilityIdentifier` it uses it as first priority and provides a short reliable target.

Example:

```swift
                    /*
                    [SalesViewController]


                                ViewOne
                                   |
                                viewTwo
                            /     |      \
                     viewThree viewFour   viewFive
                    */


let viewOne = UIView()
viewOne.accessibilityIdentifier = "rootView"
let viewTwo = UIView()
viewTwo.restorationIdentifier = "dichotomy"
let viewThree = UIButton()
viewThree.accessibilityIdentifier = "on_sale"
let viewFour = UIButton()
viewFour.accessibilityIdentifier = "new_arrivals"
let viewFive = UIButton()
viewFive.accessibilityIdentifier = "for_kids"


viewTwo.addSubview(viewThree)
viewTwo.addSubview(viewFour)
viewTwo.addSubview(viewFive)
viewOne.addSubview(viewTwo)


// viewThree target would be: UIButton[ai:SalesViewController.on_sale]
// viewFour target would be: UIButton[ai:SalesViewController.new_arrivals]


// We change the view hierarchy
viewThree.removeFromSuperview()
viewFour.addSubview(viewThree)


// viewThree target would be: UIButton[ai:SalesViewController.on_sale]
// viewFour target would be: UIButton[ai:SalesViewController.new_arrivals]
```

### restoration Identifier

If the view has `restorationIdentifier` and no `accessibilityIdentifier` then it provides a long reliable target.

```swift
/*
[SalesViewController]


            ViewOne
            /
        viewTwo
        /       \
    viewThree   viewFour
*/


let viewOne = UIView()
viewOne.restorationIdentifier = "trichotomy"
let viewTwo = UIView()
viewTwo.restorationIdentifier = "dichotomy"
let viewThree = UIView()
let viewFour = UIView()


viewTwo.addSubview(viewThree)
viewTwo.addSubview(viewFour)
viewOne.addSubview(viewTwo)


// viewThree target would be: [root]>UIView[ri:SalesViewController.trichotomy]>UIView[ri:SalesViewController.dichotomy]>UIView:eq(0)
// viewFour target would be: [root]>UIView[ri:SalesViewController.trichotomy]>UIView[ri:SalesViewController.dichotomy]>UIView:eq(1)
```

### tag

If the view has no `accessibilityIdentifier` and no `restorationIdentifier` then our SDK will use the `tag` that has been set, and will provide a long reliable target as well. tag yields the same identifiers as accessibility with a different tag

ex: `[root]>UIView[tg:444]>UIView[tg:223]>UIView:eq(1)`

### Workaround for SwiftUI: using `.overlay` to get Reliable Target Identifiers

SwiftUI views do not expose stable, introspectable identifiers at runtime. This presents a challenge when attempting to programmatically identify or interact with SwiftUI components - especially for use cases such as analytics tracking, UI testing, or runtime introspection.

To work around this limitation, you can inject a transparent overlay that contains a `UIView` with a defined `accessibilityIdentifier`. This allows tooling to detect and reference the view reliably at runtime.

Warning

Since this approach adds an actual `UIView` on top of your SwiftUI view, it intercepts user interactions. As a result, interactive components below the overlay (like buttons) will no longer receive touches unless they are explicitly layered above it.

```swift
struct AccessibleView: UIViewRepresentable {


    let accessibilityIdentifier: String


    func makeUIView(context: Context) -> UIView {
        let view = UIView()
        view.accessibilityIdentifier = accessibilityIdentifier
        return view
    }


    func updateUIView(_ uiView: UIView, context: Context) {
        uiView.accessibilityIdentifier = accessibilityIdentifier
    }
}
```

This component embeds a transparent `UIView` with the desired `accessibilityIdentifier`, which becomes visible to accessibility, UI testing, or tracking layers.

**Example:** Identifying a *non-interactive* list cell. Here is a typical usage pattern where the overlay is applied to a *non-interactive* cell. Any interactive elements (like a button) are rendered above the overlay to remain functional:

```swift
var body: some View {
    ZStack(alignment: .topTrailing) {
        VStack {
            Image(product.imageName)
                .resizable()
                .aspectRatio(contentMode: .fit)
                .frame(width: 100, height: 100)
            Text(product.title)
        }
        // This overlay makes the view identifiable at runtime
        .overlay(AccessibleView(accessibilityIdentifier: "reliableID"))


        // This button is placed above the overlay so it remains tappable
        Button("Tap Me") {
            // Perform action
        }
        .padding([.top, .trailing], 8)
    }
    .padding()
}
```

This pattern can be used when you want to identify list rows, cards, or containers that do not react to taps, but still need to be tagged for runtime analysis or inspection.

Recommendations

* Only use `.overlay(AccessibleView(accessibilityIdentifier:...))` on *non-interactive* containers.

* Ensure interactive elements are layered above the overlay if interaction is needed.

* Generate identifiers in a stable, unique per screen and predictable way, e.g.:

```swift
let reliableID = "section-\(sectionIndex)-product-\(product.id)"
```

## Exposure Metrics

In the Zoning Analysis module, Exposure Metrics provide information on the size of the display zone in the application, and data about content exposure in scrolling elements.

### Behavior for scrollable elements

Exposure Metrics data for scrolling is only calculated on the first scrollable element on the screen. Other scrolling elements in the view hierarchy are not taken into account.

This event is defined by the following content:

```javascript
ea:23, // int - event action
dx:10, // int - normalized x content offset
dy:10, // int - normalized y content offset
du: 5, // int - duration of the movement (in milliseconds)
```

#### Exclude `UIScrollView` type object from Exposure Metrics

Limitations

* Exclusion mechanism is available for UIKit only, for objects inheriting from `UIScrollView`.

- Swift

  ```swift
  let scrollView = UIScrollView() // or UICollectionView, UITableView or any class inheriting from UIScrollView
  scrollView.excludeFromExposureMetrics()
  ```

- Objective-C

  ```objective-c
  UIScrollView *scrollView = [[UIScrollView alloc] init];
  [scrollView excludeFromExposureMetrics];
  ```
