Contentsquare Flutter Integration

Introduction

Flutter support is currently only accessible to customers participating in the Early Access program. For more information reach out to your Contentsquare contact.

Get Started

This plugin relies on the Contentsquare native SDKs to log events. It exposes a unified surface, but you can consult each SDK documentation for more in-depth information:

Installation

Add the contentsquare package in your pubspec.yaml:

dependencies:
  flutter:
    sdk: flutter

  contentsquare: ^0.5.3

then run

flutter pub get

iOS

To ensure the best experience, you should target 10+ iOS version. In Podfile add this line at the top of the file:

platform :ios, '10.0'

Android

The plugin works out of the box.

Start the SDK

You do not need to do anything to start the SDK. Now that the SDK is a dependency of your app, it will autostart itself when your application starts.

Validate SDK integration

Please look at the native SDK docs for validation on each platform.

Please note that iOS logs should be viewed through Console or XCode.

Flutter Setup

Wrap the entire application inside the ContentsquareRoot widget:

import 'package:flutter/material.dart';
import 'package:contentsquare/contentsquare.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  
  Widget build(BuildContext context) {
    return ContentsquareRoot(     // <-- Wrap the entire app inside the ContentsquareRoot widget imported from 'package:contentsquare/contentsquare.dart'
      child: MaterialApp(
        title: 'My Flutter App',
        home: Home()
      ),
    );
  }
}

In-app features

Alongside its tracking capabilities, the SDKs embed some features aimed at Contentsquare users such as Snapshot Capture & SDK Logs.

Implement in-app features (iOS Only)

This implementation phase is required on the iOS side only (on Android, there is nothing to do).

In order to allow Contentsquare users to enable in-app features, you must perform 2 implementation tasks:

  1. Add the custom URL scheme in your app Info
  2. Call the SDK when the app is launched via a deeplink

1. Add the custom URL scheme in your app Info

You have to allow your app to be opened via a custom URL scheme which can be done using one of the following methods:

[tab] Xcode
  1. Open your project settings
  2. Select the app target
  3. Select the Info settings
  4. Scroll to URL Types
  5. Set the URL scheme to cs-$(PRODUCT_BUNDLE_IDENTIFIER)
[tab] Text editor
  1. Open the Info.plist of your project
  2. Add the following snippet:

    <key>CFBundleURLTypes</key>
    <array>
       <dict>
           <key>CFBundleURLSchemes</key>
           <array>
               <string>cs-$(PRODUCT_BUNDLE_IDENTIFIER)</string>
           </array>
       </dict>
    </array>
    
[tabend]

In ios/Runner/AppDelegate.swift, add a new method to the AppDelegate class:

override func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
    Contentsquare.handle(url: url)
    return super.application(app, open: url, options: options)
  }

Here is what your AppDelegate class should look like after adding the in-app features:

import UIKit
import Flutter
import ContentsquareModule

@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
  override func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
  ) -> Bool {
    GeneratedPluginRegistrant.register(with: self)
    return super.application(application, didFinishLaunchingWithOptions: launchOptions)
  }

  // Add this method:
  override func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
      Contentsquare.handle(url: url)
      return super.application(app, open: url, options: options)
  }
}

Enable in-app features

In-app features can be enabled in different ways:

Scan the QR Code

If you have access to the Contentsquare platform, you can open the in-app features modal from the menu and scan the QR code displayed with your phone.

Take a look at the native documentation for further information.

Alternative methods

We provide alternative methods to enable in-app features especially for iOS Simulator and Android Emulator.

[tab] Android

Please follow the Native Android SDK documentation for Enable in-app features

[tab] iOS

Please follow the Native iOS SDK documentation for Enable in-app features

Debugging & Logging

Contentsquare provides Logging capabilities that allow you to see the raw event data logged by your app. This is very useful for validation purposes during the instrumentation phase of development and can help you discover errors and mistakes in your analytics implementation and confirm that all events are being logged correctly.

[tab] Android

Please follow the Native Android SDK documentation for Debugging & Logging

[tab] iOS

Please follow the Native iOS SDK documentation for Debugging & Logging

Snapshot Capture

In order to unlock the full data-visualization capabilities of Contentsquare, the SDK provides a way to capture snapshots of your app screens. These snapshots can only be taken by Contentsquare's users on their device. They are not captured from your end-users device. It means your Personal Data is safe, as long as you use a test user account.

For snapshot capture to work, the session has to be tracked (included in tracked users and not opted-out) and a first screenview event has to be sent before.

[tab] Android

Please follow the Native Android SDK documentation for Snapshot Capture

[tab] iOS

Please follow the Native iOS SDK documentation for Snapshot Capture

Usage

The plugin can be used through a Contentsquare class instance:

import 'package:contentsquare/contentsquare.dart';

Contentsquare contentSquare = Contentsquare();

Since all the functions are using a PlatformChannel to send request to the native platform, all functions are Future and should be awaited and caught.

Contentsquare contentsquare = Contentsquare();
try {
  final _userId = await contentsquare.getUserId();
  setState(() {
    userId = _userId;
  });
} on PlatformException catch (e) {
    // Handle error message
}

Privacy

The Contentsquare React Native Bridge & SDKs are compliant with the Play Store & App Store Privacy guidelines as well as with the EU General Data Protection Regulation (GDPR).

Please consult our Privacy Center and Privacy Policy.

Even though Contentsquare only collects usage data on your app, we will consider every new user to be opted-out. To start tracking, the Opt-in API must be called.

If for some reason you think that securing user consent is not required for your app, discuss it during the Implementation process with your main contact.

Opt-in

Use the Opt-in API to get user consent. Calling this API will generate a user ID and initiate tracking.

await contentSquare.optIn();

Opt-Out

Permanently breaking the link and stopping all data collection.

When this API is called, tracking stops immediately, all settings are reset (session number, page number, user ID...) and all files related to Contentsquare tracking are deleted. Contentsquare will never track and collect any data from the user's phone unless the Opt-in API is called again.

await contentSquare.optOut();

Forget me

Permanently breaking the link between the collected data and actual user.

This resets all settings (session number, page number, user ID...) and deletes all files related to Contentsquare tracking from the user's device. If the user is opted in, next time the user starts the app, the SDK will re-start its collection mechanisms as if this was the first ever run for a new user, under a new user ID. Configurations will be fetched from the server and application tracking will be on.

'Forget me' only works when user is opted in.

await contentSquare.forgetMe();

Give me my data

We allow the client to provide to their users their Contentsquare user ID.

This ID is a non binding identifier which can be used to make a data request to Contentsquare. You are able to get an ID only if the user is not Opted-out.

final userId = await contentSquare.getUserId();

Stop / Resume Tracking

Although we do not gather any humanly readable text from the user's screens, we understand that there may be some areas that you want to completely exclude from tracking. For this reason, we also support stopping and resuming the complete tracking mechanism.

await contentSquare.stopTracking();

await contentSquare.resumeTracking();

Track Screens

Contentsquare aggregates user behavior and engagement at the screen level. To do so, it is required to track screen transitions by calling a dedicated API. When the API is called, the SDK logs a screenview event that identifies the new screen with the screen name provided.

Sessions without at least one screenview will be discarded.

await contentSquare.send('ScreenName');

Screen name handling

The screen name length is not limited on the SDK side. However, the limit is 2083 characters on the server side.

Screenview after app in background

The SDK triggers a screenview automatically after the app is put in background and foreground, as long as a screenview with a screen name has been triggered previously. It will use the last screen name set.

Implementation recommendations

From a functional standpoint, we expect a screenview to be sent:

  • when the screen appears
  • when a modal/pop-up is closed and the user is back on the screen
  • when the app is put in the foreground (after an app hide)

How to name screens

As a general rule, it is recommended to have a number of distinct screen names under 100. As they are used to map your app in Contentsquare, you will want something comprehensive.

Separate words with space, dash or underscore characters

If you want to generate screen names including more than one word, it is best to separate them and to do so using space, dash or underscore characters. Contentsquare handles automatically the formatting for them.

Example: For a sub-category list of a retail app, use Home & Living - Home Furnishings instead of homeLivingHomeFurnishings.

Use screen template/layout names

As a general recommendation, it is recommended to use names referring to the screen template/layout rather than referring to the specific content (data). This will help:

to keep the number of distinct screen names low and therefore make Contentsquare easier to use remove the risk of sending Personal Data to Contentsquare List of screen types falling into that category: Product detail, Event detail, Conversation/Chat, User profile...

Multiple layouts/states for one screen

In some cases, there will be screen that can have different layouts/states depending on the user context. In this situation, it would be interesting to append the layout/state value to the screen name. Examples:

  • Home screen of a travel app adapting its layout on the user context:
StateScreen name
No trip plannedHome - no trip
Trip plannedHome - trip planned
Trip about to startHome - upcoming trip
Trip in progressHome - trip in progress
  • Product detail screen of an e-commerce app with different layouts depending on the type of product:
StateScreen name
Default templateProduct detail
Template with suggested productsProduct detail - Suggestions
Template with bundled productsProduct detail - Bundle

Track Transactions

To associate a user's session with their potential purchases (and corresponding revenue), you must send the transaction via a dedicated API. For each transaction, we send:

  • price (double, required)
  • currency (String, required)
  • transactionId (String, optional)
await contentsquare.sendTransaction(430, 'EUR', "testId");

You can also use the Currency helper to get the list of ISO 4217 currencies.

await contentsquare.sendTransaction(430, Currency.EUR.toStr());

If the currency passed doesn't match our supported currencies, the SDK will send a currency value of "-1". It will be processed as the default currency of the project.

Each transaction must only be sent once. A common mistake is to trigger the sending when the confirmation screen is displayed. This leads to triggering the transaction each time the user puts the app in background and then in foreground on the confirmation screen.

Track Dynamic Variables

General principles

Usage

Dynamic variables are additional information on the session that can be used to segment sessions.

For example, they can include information on the A/B Test variations displayed to the current user.

Limits
On the server side
  • It is possible to save up to 40 distinct dynamic variable keys per screen view. If more are received, only the first 40 keys will be kept.
  • If you are using the same key twice, the last value associated with the key will be recorded.
On the SDK side
  • Every dynamic variable is composed of a pair of key (max. 50 characters) and value (max. 255 characters string or number of type Long between 0 and 2^32 -1). In case these maximums length are reached, the SDK will automatically trim the exceeding characters.
  • If key or value are empty, the SDK will instead send the literal string "cs-empty".

Defining dynamic variables

To define and send a dynamic variable, you just need to use the following API. For each dynamic variable, we send:

  • key (String, required)
  • stringValue (String, optional)
  • intValue (int, optional)

Note that exactly one of stringValue or intValue should be used at the same time.

Also, in order for you to be able to handle such errors happening when we try to send the dynamic variable, you should wrap your call in a try/catch as explained in the Usage part.

await contentsquare.sendDynamicVar('my key 1', stringValue: 'testValue');

await contentsquare.sendDynamicVar('my key 1', intValue: 12);

Type of the value — The value can be either a number or a string. For each case, available features won't be the same in the Contentsquare app:

  • for int, you will be able to do some algebra. Example: sessions with dynamic variable key = "numberOfFriends" and value >= 10
  • for String, auto-completion and Regular Expression will be available. Example: sessions with dynamic variable key = "accountType" and value is "Premium"

Sending a dynamic variable for each session

You may want to send a dynamic variable for each session (like the user's country or store). While triggering the dynamic variable at app launch will cover most cases, it will not be enough. A session can start when the app is put in foreground, after staying in background for more than 30 minutes. See Session definition section for more information.

That is why we also recommend sending such dynamic variable every time the app enters foreground.

You can use didChangeAppLifecycleState method to detect foreground and trigger a dynamic variable.

How the SDK works

Initialization

The way our SDK works is by auto-starting with the application launch. While native SDKs can intercept gesture events automatically, Flutter apps require to wrap the application into a ContentsquareRoot widget.

Configuration

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

Tracking

The SDK monitors the application lifecycle events and the view hierarchy, and generates analytics 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.

Sending data

[tab] Android

Please checkout the Native Android SDK documentation for Sending data

[tab] iOS

Please checkout the Native iOS SDK documentation for Sending data

Session definition

A session represents a single period of user interaction in the app. In Contentsquare, a session ends when the user has spent a certain amount of time outside the app. The SDK checks this when an app start or app show event is detected. This time spent outside the app, ending a session is set to 30 minutes by default. But it can be changed if it is requested.

If the app is put in the background or killed (intentionally by the user or by the OS) but the user comes back within 30 minutes, it will not end the session. These events are considered to be part of a session.

Security

Transmission and hosting

Our server side uses HTTPS to makes sure that data is encrypted in transport.

Our hosting solution is a PCI DSS Level 1 Service Provider.

What is not recorded

  • Passwords from password fields
  • Geo location information
  • Information from contacts, emails, or any other user identifiable information.
  • Text from labels, buttons, and any other widget which contains text
  • Accessibility information from any widget which the user interacts with
  • Labels printed on the screen of any kind.

Compatibility

Each version of the Contentsquare Flutter SDK will be linked to specific, fixed versions of each native SDK. In order to make that clear, here is a compatibility table to consider when choosing your version of the bridge (refer to the Flutter SDK Changelog to know what the changes are for each version).

Contentsquare Flutter SDK VersioniOS SDK VersionAndroid SDK VersionMinimum Flutter Version
0.1.04.3.04.1.02.0.0
0.2.04.6.04.3.02.0.0
0.2.14.6.04.3.02.0.0
0.2.24.10.04.3.02.0.0
0.2.34.10.14.7.02.0.0
0.3.04.11.04.7.02.0.0
0.4.04.11.04.7.02.0.0
0.4.14.11.04.8.02.0.0
0.4.24.12.04.8.02.0.0
0.5.04.13.04.9.02.0.0
0.5.14.13.04.9.02.0.0
0.5.24.13.04.9.02.0.0
0.5.34.13.04.11.02.0.0

Troubleshooting

⚠️ WARNING: SwiftContentsquarePlugin was registered twice

There is a known issue with the background_locator plugin which causes Contentsquare plugin to be registered twice. This issue has been mitigated and you can ignore this warning.

If you are not using the background_locator plugin and encounter this warning, please contact us.

Requests are failing

In order for Contentsquare to work, you need to make sure the following endpoints are not blocked by your network (VPN):

RequestEndpointDetail
Config filehttps://mobile-production.content-square.netSee Configuration
Analytics data (EU)https://m.csqtrk.netSee Sending data
Analytics data (US)https://m-aus1.contentsquare.netSee Sending data
Session Replay data (EU)https://ka-aeu1.contentsquare.netSee Session Replay requests
Session Replay data (US)https://ka-aus1.contentsquare.netSee Session Replay requests
Snapshot (EU)https://s.contentsquare.netSee Snapshot capture
Snapshot (US)https://s-aus1.contentsquare.netSee Snapshot capture

Changelog