Session Replay

A newer version of this documentation is available. Swtich to the latest version docs.

As session collection will start at the 1st screenview event, it is required to have screen tracking implemented. Make sure to follow the Flutter Track screens section.

Updating to latest SDK version

Section titled Updating to latest SDK version

In order to enable Session Replay in your app and get the most stable version, it is required to upgrade the SDK to its latest version.

If you are in the process of implementing the SDK for the 1st time (or choose to take this update as an opportunity to review your Privacy related implementation), make sure to follow the Flutter Privacy section.

Enable Session Replay on your device

Section titled Enable Session Replay on your device

Since not all sessions are collected (depending on the percentage set in Contentsquare back-office), we have implemented an option to force replay collection on your device for testing and debugging purposes. This option can be enabled from the in-app features settings:

  1. Enable in-app features
  2. Open in-app features settings with a long press on the snapshot button
  3. In the “Session replay settings” section, enable session Replay
  4. Kill the app
  5. Start app, a new session is starting with Session Replay enabled

How do I know if Session Replay is enabled?

Section titled How do I know if Session Replay is enabled?

There are 2 places where you can check if Session Replay is enabled:

In the logs: The log Session Recording is starting will confirm that Session Replay is enabled.

In in-app features settings: Below the “Enable “will start at next app start” (see below Access the replay), you will either see:

  • No replay link available which means Session Replay is not running for the current session
  • Get Replay link which means Session Replay is running for the current session

The session can be accessed by tapping on Get replay link button from the in-app features settings:

The replay will be available within 5 minutes. Only the “ended screen views” are processed (we know a screenview is ended when we start receiving data for the next screenview). This means that you will be able to replay your session up to the previous screenview if the session is still running.

It is required to send us the custom fonts you use in your app so we can use them to render text properly.

Upload the fonts in otf, ttf, woff or woff2 format directly from the player as described in Managing iOS Fonts In The Player on the Help Center.

The Session Replay feature collects every interaction of your users with your app. In order to respect the user’s right to privacy, the Contentsquare SDK :

  • Masks potentially user sensitive data by default (Explained here: Default masking)
  • Allows you to control which part of the user interface is collected via our masking and un-masking APIs.

Masking depends on the type of element:

  • Images are not collected, a placeholder is sent instead of the content; the “IMG” placeholder will be displayed in the frame of the element.
  • Text: text is replaced by “la” repeated as many times as needed to equal the original character count. White characters are preserved. For instance the lazy fox is collected as lal lala ala. All other visual properties are collected (text color, background color, alignment, etc.).
  • TextFields: same as Text
  • For all other types: no specific data is collected but visual properties are collected.

If you think a specific element can reveal personal data from one of these properties you have to mask it using one of the method presented below. A good way to check how a view is rendered in the Session Replay is to navigate to the desired view with the CS SDK running then use the quick replay link.

Below you can see an example of a masked view:

Original -> Replay fully unmasked -> Replay fully masked

By default all images, text and TextFields will be masked.

SessionReplayMaskingScope is a Flutter widget that applies specified masking rules to its descendant widgets. This can be useful when you want to mask sensitive information during session replays, such as user data or private texts.

MaskingConfig is a class that determines what type of content should be masked during session replays. You can customize the masking behavior by setting the masking options for texts, text fields, and images.

How to use SessionReplayMaskingScope

Section titled How to use SessionReplayMaskingScope

To use SessionReplayMaskingScope, wrap the desired widget(s) with the SessionReplayMaskingScope widget and provide a MaskingConfig object to configure the masking rules.

Here’s a simple example of how to use SessionReplayMaskingScope:

SessionReplayMaskingScope(
maskingConfig: MaskingConfig.maskAll(),
child: const Text('Hello world'),
);

In this example, MaskingConfig.maskAll() is used to mask the Text widget.

You can also use nested SessionReplayMaskingScope widgets to apply different masking rules to different parts of your widget tree. Here’s an example:

SessionReplayMaskingScope(
maskingConfig: MaskingConfig.maskAll(),
child: Column(
children: [
const Text('This text will be masked'),
SessionReplayMaskingScope(
maskingConfig: MaskingConfig(maskTexts: false),
child: Text('This text will NOT be masked'),
),
],
),
);

In this example, the first Text widget will be masked, while the second one will not be masked due to the maskTexts: false option in the nested SessionReplayMaskingScope.

MaskingConfig allows you to configure the masking behavior for different types of content:

  • maskTexts: If set to true, any widget that creates a RenderParagraph (e.g., Text, RichText) will be masked.
  • maskTextFields: If set to true, any widget that creates a RenderEditable (e.g., TextFormField, TextField) will be masked.
  • maskImages: If set to true, any widget that creates a an image (e.g., Image, BoxDecoration.image) will be masked.

You can create a custom MaskingConfig object by using the constructor:

const MaskingConfig({
bool maskTexts,
bool maskImages,
bool maskTextFields,
})

For easier readability and convenience, you can use the following factory constructors:

  • MaskingConfig.maskAll(): Returns a MaskingConfig object with all values set to true, meaning everything will be masked.
  • MaskingConfig.unMaskAll(): Returns a MaskingConfig object with all values set to false, meaning nothing will be masked.

If the masking parameter is not specified, the value is inherited from the nearest SessionMaskingScope widget in the widget tree.

When using SessionReplayMaskingScope and MaskingConfig, it’s essential to understand the priority of masking rules in the widget tree. The masking rules specified in a SessionReplayMaskingScope widget will affect its descendants, and if nested scopes are used, the innermost (closest) scope takes precedence.

The priority of masking rules is as follows:

  1. The MaskingConfig object provided to a SessionReplayMaskingScope widget sets the masking rules for its child and descendants.
  2. The MaskingConfig object provided to the ContentsquareRoot widget sets the default masking rules for the entire app.

If the masking parameter is not specified, the value is inherited from the nearest SessionMaskingScope widget in the widget tree.

Sessions can be collected for Session Replay if the Session Replay feature has been enabled for your project and the session matches the collection criteria.

The following conditions will have an impact on which sessions will be collected:

  • User consent: The users have given their consent (if required)
  • Collection rate: The session is being drawn for collection (see Collection Rate below)
  • Compatibility: The OS version is supported.
  • App version: The app version is not part of the block list (see App version block list below)

Note that, when Session Replay is turned off, no content specific to Session Replay is collected whatsoever. Also note that the standard Contentsquare analytics tracking remain unaffected by this.

Session Replay collection is based on a percentage of the total sessions. By default Session Replay collection is disabled.

During the early access phase, the percentage of collected sessions will be set to 1% at the beginning. It will then be adjusted according to:

  • The traffic on your app
  • The volume of collected sessions set in the contract

Refer to Compatibility.

The Contentsquare team can add versions of your app in the block list to make sure Session Replay does not start on these versions. This can be useful when a problem is discovered on a specific version of your app, such as a Personal Data for which the masking was forgotten. This allows to keep Session Replay working on the other app versions (especially the new ones with the fix).

The SDK monitors the application lifecycle events and the view hierarchy, and generates Session Replay data from the behavior of the app, the content of the screen and the interaction of the user. 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.

By default, Session Replay data can be sent over cellular network. If the particular context of your app and/or users requires a very limited impact on cellular data consumption, sending data over cellular network can be completely disabled. Once disabled, data will be sent when the device is using Wi-Fi.

Reach out to your Contentsquare contact that will make the adjustment in your project’s configuration.

Before being sent, data is stored in local files on disk up to 30MB on iOS and 20MB on Android. If the limit is reached, Session Replay is stopped. It will restart at the next app launch once the SDK is able to send the data.

The maximum request size is 1 MByte.

Requests are sent:

We always strive to be non-intrusive, and transparent to the developers of the client app. We apply this rule on the performance as well. These are the technical specifics we can share on performance, if you have any questions feel free to reach out to us.

Most operations related to Session Collection are performed on background threads, for the impact on the main thread to be minimal.

We also set up mechanisms that stop the Session Collection if we detect the feature is using too much memory.

We defined our network strategy to have the lesser impact on CPU and battery of the users devices. We measure these impacts, using Apple Dev Tools, for each release of our SDK.

Session Collection will also stop if we use up to 30Mbytes of local storage.

  • Currently Session Replay is not supported on Android.
  • There is no way to retrieve the Session Replay link programmatically at this time.
  • Different quality levels are not available for the moment.
  • Icons currently are not supported - rectangle shown instead.
  • Custom fonts are currently not supported.
  • Unmasked images impacts performance - we do not recommend using it in production for now.
  • Images set by Ink.image right now are not supported.

Requests are not sent from an Android emulator / iOS simulator

Section titled Requests are not sent from an Android emulator / iOS simulator

If you struggle to watch a replay collected on an emulator/simulator, it may be due to some network constraints applied on your computer (VPN, company network restrictions, etc.). Check your configuration and/or use a real device.

See following sections for more information about our endpoints and requests:

Very long session on an Android emulator / iOS simulator

Section titled Very long session on an Android emulator / iOS simulator

It is important to remember that an app kill does not end a session. See Session definition for more information. If you leave the simulator/emulator running with the app in foreground, the session will not end, even if you are inactive. To better reflect actual end user behavior and avoid unusually long sessions (last hours), put the app in background or kill it.