diff --git a/cpp/current.md b/cpp/current.md index fe624e47..14e9180b 100644 --- a/cpp/current.md +++ b/cpp/current.md @@ -1,20 +1,19 @@
- This document will guide you through the process of SDK installation and it applies - to version 23.2.X. + This documentation is for the Countly CPP SDK version 23.2.X. The SDK source + code repository can be found + here.
+Supported Platforms are Windows, GNU/Linux, and Mac OS X.
- It is an open-source SDK, you can take a look at our SDK code in the - Github repo -
-- Supported Platforms: Windows, GNU/Linux, and Mac OS X. + To examine the example integrations please have a look + here.
@@ -22,26 +21,26 @@ to run on most platforms. To build this SDK, you need:
First, clone the repository with its submodules:
git clone --recursive https://github.com/Countly/countly-sdk-cpp
+ git clone --recursive https://github.com/Countly/countly-sdk-cpp
If submodules in your project are empty you can run this command at root of your project:
git submodule update --init --recursive
+ git submodule update --init --recursive
If you want to use SQLite to store session data persistently, build sqlite:
# assuming we are on project root
+ # assuming we are on project root
cd vendor/sqlite
cmake -D BUILD_SHARED_LIBS=ON -B build . # out of source build, we don't like clutter :)
# we define `BUILD_SHARED_LIBS` because sqlite's cmake file compiles statically by default for some reason
@@ -50,14 +49,14 @@ make # you might want to add something like -j8 to parallelize the build process
The cmake build flow is pretty straightforward:
# assuming we are on project root again
+ # assuming we are on project root again
ccmake -B build . # this will launch a TUI, configure the build as you see fit
cd build
make
In case you would also need to install the built library, check for more
information
- here.
+ here.
Build with the option COUNTLY_BUILD_TESTS and
@@ -67,9 +66,10 @@ make
project.
cmake -DCOUNTLY_BUILD_SAMPLE=ON -DCOUNTLY_BUILD_TESTS=ON -DCOUNTLY_USE_SQLITE=ON -B build . # or do it interactively with cmake
+ cmake -DCOUNTLY_BUILD_SAMPLE=ON -DCOUNTLY_BUILD_TESTS=ON -DCOUNTLY_USE_SQLITE=ON -B build . # or do it interactively with cmake
cd build
-make ./countly-tests # run unit test
make ./countly-sample # run sample app
+make ./countly-tests # run unit test
+make ./countly-sample # run sample app
The shortest way to initiate the SDK is with this code snippet:
-cly::Countly& countly = cly::Countly::getInstance();
countly.setDeviceID("test-device-id");
countly.start("YOUR_APP_KEY", "https://try.count.ly", 443, true);
+cly::Countly& countly = cly::Countly::getInstance();
+countly.setDeviceID("test-device-id");
+countly.start("YOUR_APP_KEY", "https://try.count.ly", 443, true);
Here, you have to provide your appKey, and your Countly server URL. Please check - here + here for more information on how to acquire your application key (APP_KEY) and server URL.
@@ -99,33 +101,9 @@ make ./countly-tests # run unit testIf you are in doubt about the correctness of your Countly SDK integration, you can learn more about methods to verify it from - here. + here.
-- The first thing you should do while integrating our SDK is to enable logging. - If logging is enabled, then our SDK will print out debug messages about its internal - state and about encountered problems. -
-
- Set setLogger(logger_function) on the Counlty object
- to enable logging:
-
void printLog(cly::Countly::LogLevel level, const string& msg) {...}
...
void (*logger_function)(cly::Countly::LogLevel level, const std::string& message);
logger_function = printLog;
cly::Countly::getInstance().setLogger(logger_function);
-- All tracked information is tied to a "device ID", which is used as a unique identifier - of your users. -
-- You have to specify the device ID by yourself (it has to be unique for each of - your users). It may be an email or some other internal ID used in your system's - internal logic. -
-cly::Countly::getInstance().setDeviceID("UNIQUE_DEVICE_ID");
-
In its unconfigured state, the SDK stores everything in memory.
@@ -140,7 +118,24 @@ make ./countly-tests # run unit test
make ./countly-sample # run sample ap
To access the Countly Global Instance use the following code snippet:
-cly::Countly::getInstance().
+cly::Countly::getInstance()
++ The first thing you should do while integrating our SDK is to enable logging. + If logging is enabled, then our SDK will print out debug messages about its internal + state and about encountered problems. +
+
+ Set setLogger(logger_function) on the Counlty object
+ to enable logging:
+
void printLog(cly::Countly::LogLevel level, const string& msg) {...}
+...
+
+void (*logger_function)(cly::Countly::LogLevel level, const std::string& message);
+logger_function = printLog;
+cly::Countly::getInstance().setLogger(logger_function);
+
The Countly SDK for C++ can collect
@@ -151,33 +146,49 @@ make ./countly-tests # run unit test
make ./countly-sample # run sample ap
In the SDK all crash-related functionalities can be browsed from the returned
interface on:
countly.crash().
+countly.crash()
You might catch an exception or similar error during your app’s runtime. You may also log these handled exceptions to monitor how and when they are happening. To log handled exceptions use the following code snippet:
-/*any additional info can be provided as a segmentation*/
std::map<std::string, std::string> segmentation = {
{"platform", "ubuntu"},
{"time", "60"},
};
/*should create the crashMetrics map*/
std::map<std::string, std::any> crashMetrics;
/*mandatory values*/
crashMetrics["_os"] = "Android"; # your OS info
crashMetrics["_app_version"] = "1.22";
/*any optional info*/
crashMetrics["_cpu"] = "armv7";
countly.crash().recordException("title", "stackTrace", true, crashMetrics, segmentation);
+/*any additional info can be provided as a segmentation*/
+std::map<std::string, std::string> segmentation = {
+ {"platform", "ubuntu"},
+ {"time", "60"},
+};
+
+/*should create the crashMetrics map*/
+std::map<std::string, std::any="std::any"> crashMetrics;
+
+/*mandatory values*/
+crashMetrics["_os"] = "Android"; # your OS info
+crashMetrics["_app_version"] = "1.22";
+
+/*any optional info*/
+crashMetrics["_cpu"] = "armv7";
+
+countly.crash().recordException("title", "stackTrace", true, crashMetrics, segmentation);
recordException expects the parameters below:
title - a string that describes the exception.
stackTrace - a string that describes the contents of the call
stack.
fatal - set true if the error is fatal.
crashMetrics - key/values contain device information e.g., app
version, OS.
segments - custom key/values to be reported.
std::map<std::string, std::any> crashMetrics;
/*mandatory values*/
crashMetrics["_os"] = "Android"; /*your OS info*/
crashMetrics["_app_version"] = "22.06.1"; /*SDK version*/
/*optional values*/
crashMetrics["_os_version"] = "4.1";
crashMetrics["_manufacture"] = "Samsung"; /*may not be provided for ios or be constant, like Apple*/
crashMetrics["_device"] = "Galaxy S4"; /*model for Android, iPhone1,1 etc for iOS*/
crashMetrics["_resolution"] = "1900x1080"; /*SDK version*/
crashMetrics["_cpu"] = "armv7"; /*type of cpu used on device (for ios will be based on device)*/
crashMetrics["_opengl"] = "2.1"; /*version of open gl supported*/
crashMetrics["_ram_current"] = 1024; /*in megabytes*/
crashMetrics["_ram_total"] = 4096; /*in megabytes*/
crashMetrics["_disk_current"] = 3000; /*in megabytes*/
crashMetrics["_disk_total"] = 10240; /*in megabytes*/
crashMetrics["_bat"] = 99; /*battery level from 0 to 100*/
crashMetrics["_orientation"] = "portrait"; /*in which device was held, landscape, portrait, etc*/
crashMetrics["_root"] = false; /*true if device is rooted/jailbroken, false or not provided if not*/
crashMetrics["_online"] = false; /*true if device is connected to the internet (WiFi or 3G), false or not provided if not connected*/
crashMetrics["_muted"] = false; /*true if volume is off, device is in muted state*/
crashMetrics["_background"] = false; /*true if app was in background when it crashed*/
crashMetrics["_run"] = 2000; /*running time since app start in seconds*/
+std::map<std::string, std::any> crashMetrics;
+
+/*mandatory values*/
+crashMetrics["_os"] = "Android"; /*your OS info*/
+crashMetrics["_app_version"] = "22.06.1"; /*SDK version*/
+
+/*optional values*/
+crashMetrics["_os_version"] = "4.1";
+crashMetrics["_manufacture"] = "Samsung"; /*may not be provided for ios or be constant, like Apple*/
+crashMetrics["_device"] = "Galaxy S4"; /*model for Android, iPhone1,1 etc for iOS*/
+crashMetrics["_resolution"] = "1900x1080"; /*SDK version*/
+crashMetrics["_cpu"] = "armv7"; /*type of cpu used on device (for ios will be based on device)*/
+crashMetrics["_opengl"] = "2.1"; /*version of open gl supported*/
+crashMetrics["_ram_current"] = 1024; /*in megabytes*/
+crashMetrics["_ram_total"] = 4096; /*in megabytes*/
+crashMetrics["_disk_current"] = 3000; /*in megabytes*/
+crashMetrics["_disk_total"] = 10240; /*in megabytes*/
+crashMetrics["_bat"] = 99; /*battery level from 0 to 100*/
+crashMetrics["_orientation"] = "portrait"; /*in which device was held, landscape, portrait, etc*/
+crashMetrics["_root"] = false; /*true if device is rooted/jailbroken, false or not provided if not*/
+crashMetrics["_online"] = false; /*true if device is connected to the internet (WiFi or 3G), false or not provided if not connected*/
+crashMetrics["_muted"] = false; /*true if volume is off, device is in muted state*/
+crashMetrics["_background"] = false; /*true if app was in background when it crashed*/
+crashMetrics["_run"] = 2000; /*running time since app start in seconds*/
Throughout your app, you can leave crash breadcrumbs. They are short string logs
@@ -199,10 +233,10 @@ make ./countly-tests # run unit test
make ./countly-sample # run sample ap
the crash. After a crash happens, they will be sent together with the crash report.
The following command adds a crash breadcrumb:
-countly.crash().addBreadcrumb("breadcrumb");
+countly.crash().addBreadcrumb("breadcrumb");
- An event is any type of action that you can send to a Countly instance, e.g. purchases, changed settings, view enabled, and so on, letting you get valuable information about your application. + An event is any type of action that you can send to a Countly instance, e.g. purchases, changed settings, view enabled, and so on, letting you get valuable information about your application.
There are a couple of values that can be set when recording an event. The main
@@ -214,94 +248,120 @@ make ./countly-tests # run unit test
make ./countly-sample # run sample ap
Optionally there are also other properties that you might want to set:
std::string.
+ std::string.
- Here are some examples below, showing how to record an event for a purchase with varying levels of complexity: + Here are some examples below, showing how to record an event for a purchase with varying levels of complexity:
1. Event key and count
-cly::Countly::getInstance().RecordEvent("purchase", 1);
+cly::Countly::getInstance().RecordEvent("purchase", 1);
2. Event key, count, and sum
-cly::Countly::getInstance().RecordEvent("purchase", 1, 0.99);
+cly::Countly::getInstance().RecordEvent("purchase", 1, 0.99);
3. Event key and count with segmentation(s)
-std::map<std::string, std::string> segmentation;
segmentation["country"] = "Germany";
cly::Countly::getInstance().RecordEvent("purchase", segmentation, 1);
+std::map<std::string, std::string> segmentation;
+segmentation["country"] = "Germany";
+
+cly::Countly::getInstance().RecordEvent("purchase", segmentation, 1);
4. Event key, count, and sum with segmentation(s)
-std::map<std::string, std::string> segmentation;
segmentation["country"] = "Germany";
cly::Countly::getInstance().RecordEvent("purchase", segmentation, 1, 0.99);
+std::map<std::string, std::string> segmentation;
+segmentation["country"] = "Germany";
+
+cly::Countly::getInstance().RecordEvent("purchase", segmentation, 1, 0.99);
5. Event key, count, sum, and duration with segmentation(s)
-std::map<std::string, std::string> segmentation;
segmentation["country"] = "Germany";
cly::Countly::getInstance().RecordEvent("purchase", segmentation, 1, 0.99, 60.0);
+std::map<std::string, std::string> segmentation;
+segmentation["country"] = "Germany";
+
+cly::Countly::getInstance().RecordEvent("purchase", segmentation, 1, 0.99, 60.0);
- These are only a few examples of what you can do with Events. You may go beyond those examples and use country, app_version, game_level, time_of_day, or any other segmentation of your choice that will provide you with valuable insights. + These are only a few examples of what you can do with Events. You may go beyond those examples and use country, app_version, game_level, time_of_day, or any other segmentation of your choice that will provide you with valuable insights.
It's possible to create timed events by defining a start and a stop moment.
-cly::Event event("Some event", 1);
//start some event
event.startTimer();
//wait some time
//end the timer and record event
event.stopTimer();
-cly::Countly.getInstance().addEvent(event);
+cly::Event event("Some event", 1);
+
+//start some event
+event.startTimer();
+//wait some time
+
+//end the timer and record event
+event.stopTimer();
+
+cly::Countly.getInstance().addEvent(event);
You may also provide additional information e.g segmentation, count, and sum.
-//event with count and sum
-cly::Event event("Some event", 1, 0.99);
//add segmentation to event
event.addSegmentation("country", "Germany");
...
cly::Countly.getInstance().addEvent(event);
+//event with count and sum
+cly::Event event("Some event", 1, 0.99);
+
+//add segmentation to event
+event.addSegmentation("country", "Germany");
+
+...
+
+cly::Countly.getInstance().addEvent(event);
+
The SDK handles the sessions automatically. After calling the
- start(...) method, the SDK starts the session tracking automatically and extends sessions after every 60 seconds. This value is configurable during and after initialization.
Example:
+ start(...) method, the SDK starts the session tracking automatically and extends sessions after every 60 seconds. This value is configurable during and after initialization.
+ Example:
cly::Countly::getInstance().setAutomaticSessionUpdateInterval(10);
+cly::Countly::getInstance().setAutomaticSessionUpdateInterval(10);
- The SDK ends the current session whenever the user exits from the app. + The SDK ends the current session whenever the user exits from the app.
std::string& viewID = cly::Countly::getInstance().views().openView("Home Scene");
+std::string& viewID = cly::Countly::getInstance().views().openView("Home Scene");
While tracking views manually, you may add your custom segmentation to those views like this:
-std::map<std::string, std::string> segmentation = {
{"cats", "123"},
{"moons", "9.98"},
{"Moose", "deer"},
};
std::string& viewID = cly::Countly::getInstance().views().openView("Home Scene", segmentation);
+std::map<std::string, std::string> segmentation = {
+ {"cats", "123"},
+ {"moons", "9.98"},
+ {"Moose", "deer"},
+};
+
+std::string& viewID = cly::Countly::getInstance().views().openView("Home Scene", segmentation);
+
When the screen closes you can report it to the server by using one of the following methods: @@ -325,15 +392,17 @@ cly::Countly.getInstance().addEvent(event);
When you start recording a view by calling the - openView method, it returns a view ID of type std::string. You can use this ID to close a view. + openView method, it returns a view ID of type std::string. You can use this ID to close a view.
For example:
-std::string& viewID = cly::Countly::getInstance().views().openView("Home Scene");
...
cly::Countly::getInstance().views().closeViewWithID(viewId);
+std::string& viewID = cly::Countly::getInstance().views().openView("Home Scene");
+...
+cly::Countly::getInstance().views().closeViewWithID(viewId);
- 2. Ending a view with a view name:
You may close a view by
- its name using the following method:
+ 2. Ending a view with a view name:
+ You may close a view by its name using the following method:
cly::Countly::getInstance().views().closeViewWithName("Home Scene");
+cly::Countly::getInstance().views().closeViewWithName("Home Scene");
To review the resulting view data, go to the Analytics > Views
section in your Countly server. For more information on how to utilize view tracking
@@ -349,8 +418,11 @@ cly::Countly.getInstance().addEvent(event);
In the C++ SDK the device ID is not persistent and has to be provided every time - you start the SDK. + you start the SDK:
+cly::Countly::getInstance().setDeviceID("UNIQUE_DEVICE_ID");
+In case your application authenticates users, you might want to change the initial @@ -362,7 +434,7 @@ cly::Countly.getInstance().addEvent(event); with the current device ID will be transferred (merged) into the user profile with the device ID you specified in the following method call:
-cly::Countly::getInstance().setDeviceID("new-device-id", true);
+cly::Countly::getInstance().setDeviceID("new-device-id", true);
If you integrate this method, there might be times where you might want to track information about another user that starts using your app from the same device @@ -370,7 +442,7 @@ cly::Countly.getInstance().addEvent(event); the identity of the current user (user logs out). In those cases, you can change the current device ID to a new one without merging their data. You would call:
-cly::Countly::getInstance().setDeviceID("new-device-id", false);
+cly::Countly::getInstance().setDeviceID("new-device-id", false);
Doing it this , will prevent the previous user's data to merge with the new id.
@@ -390,12 +462,12 @@ cly::Countly.getInstance().addEvent(event); user base. There are 4 fields that can be provided:@@ -403,7 +475,14 @@ cly::Countly.getInstance().addEvent(event); info will be sent to the server at the start of the user session.
Example:
-string countryCode = "us";
string city = "Houston";
string latitude = "29.634933";
string longitude = "-95.220255";
string ipAddress = "192.168.0.1";
cly::Countly::getInstance().setLocation(countryCode, city, latitude + "," + longitude, ipAddress);
+string countryCode = "us";
+string city = "Houston";
+string latitude = "29.634933";
+string longitude = "-95.220255";
+string ipAddress = "192.168.0.1";
+
+cly::Countly::getInstance().setLocation(countryCode, city, latitude + "," + longitude, ipAddress);
+
Note that the IP address would only be updated if it's set during the init process.
@@ -427,14 +506,14 @@ cly::Countly.getInstance().addEvent(event);
To download Remote Config, call updateRemoteConfig().
cly::Countly.getInstance().updateRemoteConfig();
+cly::Countly.getInstance().updateRemoteConfig();
To access the stored config, call
cly::Countly.getInstance().getRemoteConfigValue(const std::string& key).
It will return null if there isn't any config stored.
cly::Countly.getInstance().getRemoteConfigValue("Key");
+cly::Countly.getInstance().getRemoteConfigValue("Key");
It returns a value of the type json.
+ If a property is set as an empty string, it will be deleted from the user on + the server side. +
The Countly C++ SDK allows you to upload user specific data to your Countly server. @@ -450,77 +533,104 @@ cly::Countly.getInstance().addEvent(event);
The keys for predefined user data fields are as follows:
| Key | -Type | -Description | -
|---|---|---|
| name | -string | -User's full name | -
| username | -string | -User's nickname | -
| string | -User's email address | -|
| organization | -string | -User's organization name | -
| phone | -string | -User's phone number | -
| picture | -string | -URL to avatar or profile picture of the user | -
| gender | -string | -User's gender as M for male and F for female | -
| byear | -string | -User's year of birth as integer | -
| Key | +Type | +Description | +
|---|---|---|
| name | +string | +User's full name | +
| username | +string | +User's nickname | +
| string | +User's email address | +|
| organization | +string | +User's organization name | +
| phone | +string | +User's phone number | +
| picture | +string | +URL to avatar or profile picture of the user | +
| gender | +string | +User's gender as M for male and F for female | +
| byear | +string | +User's year of birth as integer | +
The SDK allows you to upload user details using the methods listed below.
Example:
-std::map<std::string, std::string> userdetail = {
{"name", "Full name"},
{"username", "username123"},
{"email", "useremail@email.com"},
{"phone", "222-222-222"},
{"picture", "http://webresizer.com/images2/bird1_after.jpg"},
{"gender", "M"},
{"byear", "1991"},
{"organization", "Organization"},
};
cly::Countly.getInstance().setUserDetails(userdetail);
+std::map<std::string, std::string> userdetail = {
+ {"name", "Full name"},
+ {"username", "username123"},
+ {"email", "useremail@email.com"},
+ {"phone", "222-222-222"},
+ {"picture", "http://webresizer.com/images2/bird1_after.jpg"},
+ {"gender", "M"},
+ {"byear", "1991"},
+ {"organization", "Organization"},
+};
+cly::Countly.getInstance().setUserDetails(userdetail);
+
The SDK gives you the flexibility to send only the custom data to Countly servers, even when you don’t want to send other user-related data.
Example:
-std::map<std::string, std::string> userdetail = {
{"Height", "5.8"},
{"Mole", "Lower Left Cheek"}
};
cly::Countly.getInstance().setCustomUserDetails(userdetail);
+std::map<std::string, std::string> userdetail = {
+ {"Height", "5.8"},
+ {"Mole", "Lower Left Cheek"}
+};
+
+cly::Countly.getInstance().setCustomUserDetails(userdetail);
+
The SDK allows you to set the user's picture URL along with other details using the methods listed below.
Example:
-std::map<std::string, std::string> userdetail = {
{"name", "Full name"},
{"picture", "http://webresizer.com/images2/bird1_after.jpg"},
};
cly::Counlty.getInstance().setUserDetails(userdetail);
+std::map<std::string, std::string> userdetail = {
+ {"name", "Full name"},
+ {"picture", "http://webresizer.com/images2/bird1_after.jpg"},
+};
+
+cly::Counlty.getInstance().setUserDetails(userdetail);
+
You may set an optional salt to be used for calculating the checksum
of requested data which will be sent with each request, using the
@@ -529,8 +639,23 @@ cly::Countly.getInstance().addEvent(event);
server is set, all requests would be checked for the validity of the
&checksum the field before being processed.
cly::Countly.getInstance().setSalt("salt");
+cly::Countly.getInstance().setSalt("salt");
+ These are the methods that lets you configure the Countly SDK: +
++ example_integration.cpp + covers basic functionalities. +
Before or after SDK starts, you can set a threshold for the number of events
@@ -538,7 +663,7 @@ cly::Countly.getInstance().addEvent(event);
are all sent to the request queue.
Example:
cly::Counlty.getInstance().setEventsToRQThreshold(10);
+cly::Counlty.getInstance().setEventsToRQThreshold(10);
When the threshold is reached, the SDK batches all the events in the event queue
and sends them to the request queue to be sent to the server in a single request.
@@ -555,7 +680,7 @@ cly::Countly.getInstance().addEvent(event);
(100 by default) and starts again at the next iteration of the update loop.
Example:
cly::Counlty.getInstance().setMaxRQProcessingBatchSize(10);
+cly::Counlty.getInstance().setMaxRQProcessingBatchSize(10);
In case you need persistent storage, you would need to build the SDK with that @@ -567,13 +692,28 @@ cly::Countly.getInstance().addEvent(event); file could be stored.
cly::Countly::getInstance().SetPath("databaseFileName.db");
+ cly::Countly::getInstance().SetPath("databaseFileName.db");
Building your SDK with SQLite would enable event and request queues to be stored persistently in your device. SDK would also try to rebuild the database file, repacking it into a minimal amount of disk space (SQLite Vacuum) every initialization.
++ User metrics information is sent to the server with every 'begin session' request + and when requesting remote config. It's possible to override this information + and provide custom metrics. For such cases, the SetMetrics() method can be used + before starting the SDK. User metrics that are provided by SetMetrics() method + are, _os, _os_version, _device, _resolution, _carrier, and _app_version respectively. + For further information about these parameters, please refer to + here. +
+The example usage of SetMetrics() would be like this:
+Countly &ct = Countly::getInstance();
+// OS, OS version, device, resolution, carrier, app version
+ct.SetMetrics("Windows 10", "10.22", "Lenovo", "800x600", "Carrier", "1.0");
+ct.start(_appKey, _serverUrl, 443, true);
C++ SDK allows users to set a custom SHA-256 method for calculating the checksum
@@ -593,28 +733,41 @@ cly::Countly.getInstance().addEvent(event);
2. Set custom SHA-256 method setSha256
For example:
--std::string customChecksumCalculator(const std::string& data) {
...
return result;
}cly::Countly& countly = cly::Countly.getInstance();countly.setSalt("salt");
countly.setSha256(customChecksumCalculator);
std::string customChecksumCalculator(const std::string& data) {
+ ...
+ return result;
+}
+
+
+cly::Countly& countly = cly::Countly.getInstance();
+
+countly.setSalt("salt");
+countly.setSha256(customChecksumCalculator);
+
+
In some cases your project might need to install Countly globally one the system.
In those situation you would also want to run the make installcommand.
As per the description, it install the countly library on the system.
For example:
-#configure the SDK build
+#configure the SDK build
cmake -DCMAKE_INSTALL_PREFIX:PATH=/usr/local -DBUILD_SHARED_LIBS=OFF -B build
-
cd build
#build the SDK
+
+cd build
+#build the SDK
make
-
#install countly on the system
+
+#install countly on the system
make install
- -
+
-
CMAKE_INSTALL_PREFIX
Install directory used by install. If “make install” is invoked or INSTALL
is built, this directory is prepended onto all install directories. This
variable defaults to '/usr/local' on UNIX and 'c:/Program Files' on Windows.
- -
+
-
BUILD_SHARED_LIBS
If present and true, this will cause all libraries to be built shared unless
the library was explicitly added as a static library.
@@ -625,11 +778,6 @@ make install
There are some data that is collected by SDK to perform their functions and implement the required features. Before any of it is sent to the server, it is stored locally. -
-
- * When sending any network requests to the server, the following things are sent
- in addition to the main data:
- - Timestamp of when the request is created
- - SDK version
- - SDK name
+ Here
+ is the collected data for the SDK.
First, clone the repository with its submodules:
git clone --recursive https://github.com/Countly/countly-sdk-cpp
+ git clone --recursive https://github.com/Countly/countly-sdk-cpp
If submodules in your project are empty you can run this command at root of your project:
git submodule update --init --recursive
+ git submodule update --init --recursive
If you want to use SQLite to store session data persistently, build sqlite:
# assuming we are on project root
+ # assuming we are on project root
cd vendor/sqlite
cmake -D BUILD_SHARED_LIBS=ON -B build . # out of source build, we don't like clutter :)
# we define `BUILD_SHARED_LIBS` because sqlite's cmake file compiles statically by default for some reason
@@ -49,7 +49,7 @@ make # you might want to add something like -j8 to parallelize the build process
The cmake build flow is pretty straightforward:
# assuming we are on project root again
+ # assuming we are on project root again
ccmake -B build . # this will launch a TUI, configure the build as you see fit
cd build
make
@@ -66,7 +66,7 @@ make
project.
cmake -DCOUNTLY_BUILD_SAMPLE=ON -DCOUNTLY_BUILD_TESTS=ON -DCOUNTLY_USE_SQLITE=ON -B build . # or do it interactively with cmake
+ cmake -DCOUNTLY_BUILD_SAMPLE=ON -DCOUNTLY_BUILD_TESTS=ON -DCOUNTLY_USE_SQLITE=ON -B build . # or do it interactively with cmake
cd build
make ./countly-tests # run unit test
make ./countly-sample # run sample app
@@ -80,7 +80,7 @@ make ./countly-sample # run sample app
The shortest way to initiate the SDK is with this code snippet:
-cly::Countly& countly = cly::Countly::getInstance();
+cly::Countly& countly = cly::Countly::getInstance();
countly.setDeviceID("test-device-id");
countly.start("YOUR_APP_KEY", "https://try.count.ly", 443, true);
@@ -118,7 +118,7 @@ countly.start("YOUR_APP_KEY", "https://try.count.ly", 443, true);
To access the Countly Global Instance use the following code snippet:
-cly::Countly::getInstance()
+cly::Countly::getInstance()
The first thing you should do while integrating our SDK is to enable logging.
@@ -129,7 +129,7 @@ countly.start("YOUR_APP_KEY", "https://try.count.ly", 443, true);
Set setLogger(logger_function) on the Counlty object
to enable logging:
void printLog(cly::Countly::LogLevel level, const string& msg) {...}
+void printLog(cly::Countly::LogLevel level, const string& msg) {...}
...
void (*logger_function)(cly::Countly::LogLevel level, const std::string& message);
@@ -146,14 +146,14 @@ cly::Countly::getInstance().setLogger(logger_function);
In the SDK all crash-related functionalities can be browsed from the returned
interface on:
-countly.crash()
+countly.crash()
Handled Exceptions
You might catch an exception or similar error during your app’s runtime. You
may also log these handled exceptions to monitor how and when they are happening.
To log handled exceptions use the following code snippet:
-/*any additional info can be provided as a segmentation*/
+/*any additional info can be provided as a segmentation*/
std::map<std::string, std::string> segmentation = {
{"platform", "ubuntu"},
{"time", "60"},
@@ -174,21 +174,21 @@ countly.crash().recordException("title", "stackTrace", true, crashMetrics, segme
recordException expects the parameters below:
- -
+
-
title - a string that describes the exception.
- -
+
-
stackTrace - a string that describes the contents of the call
stack.
- -
+
-
fatal - set true if the error is fatal.
- -
+
-
crashMetrics - key/values contain device information e.g., app
version, OS.
- -
+
-
segments - custom key/values to be reported.
@@ -202,7 +202,7 @@ countly.crash().recordException("title", "stackTrace", true, crashMetrics, segme
keys are optional, so you can add more key-value pairs to form a detailed crash
report from the available options shown below:
-std::map<std::string, std::any> crashMetrics;
+std::map<std::string, std::any> crashMetrics;
/*mandatory values*/
crashMetrics["_os"] = "Android"; /*your OS info*/
@@ -233,10 +233,10 @@ crashMetrics["_run"] = 2000; /*running time since app start in seconds*/<
the crash. After a crash happens, they will be sent together with the crash report.
The following command adds a crash breadcrumb:
-countly.crash().addBreadcrumb("breadcrumb");
+countly.crash().addBreadcrumb("breadcrumb");
Events
- An event is any type of action that you can send to a Countly instance, e.g. purchases, changed settings, view enabled, and so on, letting you get valuable information about your application.
+ An event is any type of action that you can send to a Countly instance, e.g. purchases, changed settings, view enabled, and so on, letting you get valuable information about your application.
There are a couple of values that can be set when recording an event. The main
@@ -248,88 +248,88 @@ crashMetrics["_run"] = 2000; /*running time since app start in seconds*/
<
Optionally there are also other properties that you might want to set:
- -
+
-
count - a whole numerical value that marks how many times
this event has happened. The default value for this is 1.
- -
+
-
sum - This value would be summed across all events in the
dashboard. For example, for in-app purchase events, it can be the sum of
purchased items. Its default value is 0.
- -
+
-
duration - For recording and tracking the duration of events.
The default value is 0.
- -
+
-
segments - A value where you can provide custom segmentation
for your events to track additional information. It is a key and value map.
The accepted data type for the value is
-
std::string.
+ std::string.
Recording Events
- Here are some examples below, showing how to record an event for a purchase with varying levels of complexity:
+ Here are some examples below, showing how to record an event for a purchase with varying levels of complexity:
- -
+
-
Usage 1: Times the purchase event occurred.
- -
+
-
Usage 2: Times the purchase event occurred + the total amount
of those purchases.
- -
+
-
Usage 3: Times the purchase event occurred +
- origin of the purchase.
+ origin of the purchase.
- -
+
-
Usage 4: Times the purchase event occurred +
- the total amount + origin of the purchase.
+ the total amount + origin of the purchase.
- -
+
-
Usage 5: Times the purchase event occurred +
- the total amount + origin of the purchase + the total duration of those events.
+ the total amount + origin of the purchase + the total duration of those events.
1. Event key and count
-cly::Countly::getInstance().RecordEvent("purchase", 1);
+cly::Countly::getInstance().RecordEvent("purchase", 1);
2. Event key, count, and sum
-cly::Countly::getInstance().RecordEvent("purchase", 1, 0.99);
+cly::Countly::getInstance().RecordEvent("purchase", 1, 0.99);
3. Event key and count with segmentation(s)
-std::map<std::string, std::string> segmentation;
+std::map<std::string, std::string> segmentation;
segmentation["country"] = "Germany";
cly::Countly::getInstance().RecordEvent("purchase", segmentation, 1);
4. Event key, count, and sum with segmentation(s)
-std::map<std::string, std::string> segmentation;
+std::map<std::string, std::string> segmentation;
segmentation["country"] = "Germany";
cly::Countly::getInstance().RecordEvent("purchase", segmentation, 1, 0.99);
5. Event key, count, sum, and duration with segmentation(s)
-std::map<std::string, std::string> segmentation;
+std::map<std::string, std::string> segmentation;
segmentation["country"] = "Germany";
cly::Countly::getInstance().RecordEvent("purchase", segmentation, 1, 0.99, 60.0);
- These are only a few examples of what you can do with Events. You may go beyond those examples and use country, app_version, game_level, time_of_day, or any other segmentation of your choice that will provide you with valuable insights.
+ These are only a few examples of what you can do with Events. You may go beyond those examples and use country, app_version, game_level, time_of_day, or any other segmentation of your choice that will provide you with valuable insights.
Timed Events
It's possible to create timed events by defining a start and a stop moment.
-cly::Event event("Some event", 1);
+cly::Event event("Some event", 1);
//start some event
event.startTimer();
@@ -342,7 +342,7 @@ cly::Countly.getInstance().addEvent(event);
You may also provide additional information e.g segmentation, count, and sum.
-//event with count and sum
+//event with count and sum
cly::Event event("Some event", 1, 0.99);
//add segmentation to event
@@ -356,11 +356,12 @@ cly::Countly.getInstance().addEvent(event);
Automatic Session Tracking
The SDK handles the sessions automatically. After calling the
- start(...) method, the SDK starts the session tracking automatically and extends sessions after every 60 seconds. This value is configurable during and after initialization.
Example:
+ start(...) method, the SDK starts the session tracking automatically and extends sessions after every 60 seconds. This value is configurable during and after initialization.
+ Example:
-cly::Countly::getInstance().setAutomaticSessionUpdateInterval(10);
+cly::Countly::getInstance().setAutomaticSessionUpdateInterval(10);
- The SDK ends the current session whenever the user exits from the app.
+ The SDK ends the current session whenever the user exits from the app.
View Tracking
Manual View Recording
@@ -369,12 +370,12 @@ cly::Countly.getInstance().addEvent(event);
report which views a user has visited with the duration of that visit. To report
a screen from your app to the Countly server, you can use the following method:
-std::string& viewID = cly::Countly::getInstance().views().openView("Home Scene");
+std::string& viewID = cly::Countly::getInstance().views().openView("Home Scene");
While tracking views manually, you may add your custom segmentation to those
views like this:
-std::map<std::string, std::string> segmentation = {
+std::map<std::string, std::string> segmentation = {
{"cats", "123"},
{"moons", "9.98"},
{"Moose", "deer"},
@@ -391,17 +392,17 @@ std::string& viewID = cly::Countly::getInstance().views().openView("Home Sce
When you start recording a view by calling the
- openView method, it returns a view ID of type std::string. You can use this ID to close a view.
+ openView method, it returns a view ID of type std::string. You can use this ID to close a view.
For example:
-std::string& viewID = cly::Countly::getInstance().views().openView("Home Scene");
+std::string& viewID = cly::Countly::getInstance().views().openView("Home Scene");
...
cly::Countly::getInstance().views().closeViewWithID(viewId);
- 2. Ending a view with a view name:
You may close a view by
- its name using the following method:
+ 2. Ending a view with a view name:
+ You may close a view by its name using the following method:
-cly::Countly::getInstance().views().closeViewWithName("Home Scene");
+cly::Countly::getInstance().views().closeViewWithName("Home Scene");
To review the resulting view data, go to the Analytics > Views
section in your Countly server. For more information on how to utilize view tracking
@@ -420,7 +421,7 @@ cly::Countly::getInstance().views().closeViewWithID(viewId);
you start the SDK:
- cly::Countly::getInstance().setDeviceID("UNIQUE_DEVICE_ID");
+ cly::Countly::getInstance().setDeviceID("UNIQUE_DEVICE_ID");
Changing Device ID
@@ -433,7 +434,7 @@ cly::Countly::getInstance().views().closeViewWithID(viewId);
with the current device ID will be transferred (merged) into the user profile
with the device ID you specified in the following method call:
-cly::Countly::getInstance().setDeviceID("new-device-id", true);
+cly::Countly::getInstance().setDeviceID("new-device-id", true);
If you integrate this method, there might be times where you might want to track
information about another user that starts using your app from the same device
@@ -441,7 +442,7 @@ cly::Countly::getInstance().views().closeViewWithID(viewId);
the identity of the current user (user logs out). In those cases, you can change
the current device ID to a new one without merging their data. You would call:
-cly::Countly::getInstance().setDeviceID("new-device-id", false);
+cly::Countly::getInstance().setDeviceID("new-device-id", false);
Doing it this , will prevent the previous user's data to merge with the new id.
@@ -461,12 +462,12 @@ cly::Countly::getInstance().views().closeViewWithID(viewId);
user base. There are 4 fields that can be provided:
- - Country code (two-letter ISO standard).
- - City name (must be set together with the country code).
- -
+
- Country code (two-letter ISO standard).
+ - City name (must be set together with the country code).
+ -
Latitude and longitude values, separated by a comma e.g. "56.42345,123.45325".
- - Your user’s IP address.
+ - Your user’s IP address.
Setting Location
@@ -474,7 +475,7 @@ cly::Countly::getInstance().views().closeViewWithID(viewId);
info will be sent to the server at the start of the user session.
Example:
-string countryCode = "us";
+string countryCode = "us";
string city = "Houston";
string latitude = "29.634933";
string longitude = "-95.220255";
@@ -505,14 +506,14 @@ cly::Countly::getInstance().setLocation(countryCode, city, latitude + "," + long
To download Remote Config, call updateRemoteConfig().
-cly::Countly.getInstance().updateRemoteConfig();
+cly::Countly.getInstance().updateRemoteConfig();
Accessing Remote Config Values
To access the stored config, call
cly::Countly.getInstance().getRemoteConfigValue(const std::string& key).
It will return null if there isn't any config stored.
-cly::Countly.getInstance().getRemoteConfigValue("Key");
+cly::Countly.getInstance().getRemoteConfigValue("Key");
It returns a value of the type json.
@@ -522,7 +523,8 @@ cly::Countly::getInstance().setLocation(countryCode, city, latitude + "," + long
this documentation.
- If a property is set as an empty string, it will be deleted from the user on the server side.
+ If a property is set as an empty string, it will be deleted from the user on
+ the server side.
Setting Predefined Values
@@ -531,61 +533,65 @@ cly::Countly::getInstance().setLocation(countryCode, city, latitude + "," + long
The keys for predefined user data fields are as follows:
-
-
-
- Key
- Type
- Description
-
-
- name
- string
- User's full name
-
-
- username
- string
- User's nickname
-
-
- email
- string
- User's email address
-
-
- organization
- string
- User's organization name
-
-
- phone
- string
- User's phone number
-
-
- picture
- string
- URL to avatar or profile picture of the user
-
-
- gender
- string
- User's gender as M for male and F for female
-
-
- byear
- string
- User's year of birth as integer
-
-
-
+
+
+
+
+ Key
+ Type
+ Description
+
+
+
+
+ name
+ string
+ User's full name
+
+
+ username
+ string
+ User's nickname
+
+
+ email
+ string
+ User's email address
+
+
+ organization
+ string
+ User's organization name
+
+
+ phone
+ string
+ User's phone number
+
+
+ picture
+ string
+ URL to avatar or profile picture of the user
+
+
+ gender
+ string
+ User's gender as M for male and F for female
+
+
+ byear
+ string
+ User's year of birth as integer
+
+
+
+
The SDK allows you to upload user details using the methods listed below.
Example:
-std::map<std::string, std::string> userdetail = {
+std::map<std::string, std::string> userdetail = {
{"name", "Full name"},
{"username", "username123"},
{"email", "useremail@email.com"},
@@ -603,7 +609,7 @@ cly::Countly.getInstance().setUserDetails(userdetail);
even when you don’t want to send other user-related data.
Example:
-std::map<std::string, std::string> userdetail = {
+std::map<std::string, std::string> userdetail = {
{"Height", "5.8"},
{"Mole", "Lower Left Cheek"}
};
@@ -616,7 +622,7 @@ cly::Countly.getInstance().setCustomUserDetails(userdetail);
the methods listed below.
Example:
-std::map<std::string, std::string> userdetail = {
+std::map<std::string, std::string> userdetail = {
{"name", "Full name"},
{"picture", "http://webresizer.com/images2/bird1_after.jpg"},
};
@@ -624,7 +630,7 @@ cly::Countly.getInstance().setCustomUserDetails(userdetail);
cly::Counlty.getInstance().setUserDetails(userdetail);
Security and Privacy
-Parameter Tamper Protection
+Parameter Tamper Protection
You may set an optional salt to be used for calculating the checksum
of requested data which will be sent with each request, using the
@@ -633,8 +639,18 @@ cly::Counlty.getInstance().setUserDetails(userdetail);
server is set, all requests would be checked for the validity of the
&checksum the field before being processed.
-cly::Countly.getInstance().setSalt("salt");
+cly::Countly.getInstance().setSalt("salt");
Other Features and Notes
+SDK Config Parameters Explained
+
+ These are the methods that lets you configure the Countly SDK:
+
+
+ -
+ disableAutoEventsOnUserProperties() - Disables sending events
+ on user property calls.
+
+
Example Integrations
example_integration.cpp
@@ -647,7 +663,7 @@ cly::Counlty.getInstance().setUserDetails(userdetail);
are all sent to the request queue.
Example:
-cly::Counlty.getInstance().setEventsToRQThreshold(10);
+cly::Counlty.getInstance().setEventsToRQThreshold(10);
When the threshold is reached, the SDK batches all the events in the event queue
and sends them to the request queue to be sent to the server in a single request.
@@ -664,7 +680,7 @@ cly::Counlty.getInstance().setUserDetails(userdetail);
(100 by default) and starts again at the next iteration of the update loop.
Example:
-cly::Counlty.getInstance().setMaxRQProcessingBatchSize(10);
+cly::Counlty.getInstance().setMaxRQProcessingBatchSize(10);
Setting Up SQLite Storage
In case you need persistent storage, you would need to build the SDK with that
@@ -676,7 +692,7 @@ cly::Counlty.getInstance().setUserDetails(userdetail);
file could be stored.
- cly::Countly::getInstance().SetPath("databaseFileName.db");
+ cly::Countly::getInstance().SetPath("databaseFileName.db");
Building your SDK with SQLite would enable event and request queues to be stored
@@ -694,7 +710,7 @@ cly::Counlty.getInstance().setUserDetails(userdetail);
here.
The example usage of SetMetrics() would be like this:
-Countly &ct = Countly::getInstance();
+Countly &ct = Countly::getInstance();
// OS, OS version, device, resolution, carrier, app version
ct.SetMetrics("Windows 10", "10.22", "Lenovo", "800x600", "Carrier", "1.0");
ct.start(_appKey, _serverUrl, 443, true);
@@ -717,7 +733,7 @@ ct.start(_appKey, _serverUrl, 443, true);
2. Set custom SHA-256 method setSha256
For example:
-std::string customChecksumCalculator(const std::string& data) {
+std::string customChecksumCalculator(const std::string& data) {
...
return result;
}
@@ -728,27 +744,30 @@ cly::Countly& countly = cly::Countly.getInstance();
countly.setSalt("salt");
countly.setSha256(customChecksumCalculator);
-Additional project install option
+Additional project install option
In some cases your project might need to install Countly globally one the system.
In those situation you would also want to run the make installcommand.
As per the description, it install the countly library on the system.
For example:
-#configure the SDK build
+#configure the SDK build
cmake -DCMAKE_INSTALL_PREFIX:PATH=/usr/local -DBUILD_SHARED_LIBS=OFF -B build
-
cd build
#build the SDK
+
+cd build
+#build the SDK
make
-
#install countly on the system
+
+#install countly on the system
make install
- -
+
-
CMAKE_INSTALL_PREFIX
Install directory used by install. If “make install” is invoked or INSTALL
is built, this directory is prepended onto all install directories. This
variable defaults to '/usr/local' on UNIX and 'c:/Program Files' on Windows.
- -
+
-
BUILD_SHARED_LIBS
If present and true, this will cause all libraries to be built shared unless
the library was explicitly added as a static library.
@@ -761,4 +780,4 @@ make install
the required features. Before any of it is sent to the server, it is stored locally.
Here
is the collected data for the SDK.
-
+
\ No newline at end of file
diff --git a/flutter/current.md b/flutter/current.md
index badf4e35..623f86b8 100644
--- a/flutter/current.md
+++ b/flutter/current.md
@@ -15,61 +15,80 @@
4.0, tvOS 10.0, macOS 10.14), and it requires Xcode 13.0+.
- For Android builds, this SDK requires a minimum Android version of 4.2.x (API
- Level 17).
+ For Android builds, this SDK requires a minimum Android version of 5.0 (API Level
+ 21).
For Web builds, this SDK is compatible with browsers that support ECMAScript
5. Minimum versions of major internet browsers that fully support ES5 are:
-
-
-
-
- IE
-
-
- Edge
-
-
- Firefox
-
-
- Firefox (Android)
-
-
- Opera
-
-
- Opera (Mobile)
-
-
- Safari
-
-
- Safari (iOS)
-
-
- Chrome
-
-
- Chrome (Android)
-
-
-
- 10
- 12
- 21
- 96
- 15
- 64
- 6
- 6
- 23
- 98
-
-
-
+
+
+
+
+
+ IE
+
+
+ Edge
+
+
+ Firefox
+
+
+ Firefox (Android)
+
+
+ Opera
+
+
+
+ Opera
+
+
+ (Mobile)
+
+
+
+ Safari
+
+
+
+ Safari
+
+
+ (iOS)
+
+
+
+ Chrome
+
+
+
+ Chrome
+
+
+ (Android)
+
+
+
+
+
+
+ 10
+ 12
+ 21
+ 96
+ 15
+ 64
+ 6
+ 6
+ 23
+ 98
+
+
+
+
To examine the example integrations, please have a look
here.
@@ -78,19 +97,19 @@
Add this to your project's pubspec.yaml file:
-dependencies:
+dependencies:
countly_flutter: ^25.4.0
After you can install packages from the command line with Flutter:
-flutter pub get
+flutter pub get
SDK Integration
Minimal Setup
The shortest way to initialize the SDK, if you want Countly SDK to take care
of device ID seamlessly, is to use the code below.
-// Create the configuration with your app key and server URL
+// Create the configuration with your app key and server URL
CountlyConfig config = CountlyConfig(SERVER_URL, APP_KEY);
// Initialize with that configuration
@@ -123,16 +142,16 @@ Countly.initWithConfig(config).then((value){
SDK Data Storage
SDK data storage locations are platform-specific:
- -
+
-
For iOS, the SDK data is stored in the Application Support
Directory in a file named "Countly.dat"
- -
+
-
For Android, the SDK data is stored in SharedPreferences.
A SharedPreferences object points to a file containing key-value pairs and
provides simple reading and writing methods.
- -
+
-
For Web, the SDK data is stored in Local Storage. It can
be modified to use memory only or cookies.
@@ -145,7 +164,7 @@ Countly.initWithConfig(config).then((value){
We advise doing this while implementing Countly features in your application.
-
CountlyConfig config = CountlyConfig(SERVER_URL, APP_KEY);
+CountlyConfig config = CountlyConfig(SERVER_URL, APP_KEY);
config.setLoggingEnabled(true);
For more information on where to find the SDK logs you can check the documentation
@@ -167,7 +186,7 @@ config.setLoggingEnabled(true);
If you want to enable automatic unhandled crash reporting, you need to call this
before init:
-CountlyConfig config = CountlyConfig(SERVER_URL, APP_KEY);
+CountlyConfig config = CountlyConfig(SERVER_URL, APP_KEY);
config.enableCrashReporting()
By doing that it will automatically catch all errors that are thrown from within
@@ -184,7 +203,7 @@ config.enableCrashReporting()
The following call will add the provided segmentation to all recorded crashes.
Use the following function for this purpose:
-CountlyConfig config = CountlyConfig(SERVER_URL, APP_KEY);
+CountlyConfig config = CountlyConfig(SERVER_URL, APP_KEY);
config.setCustomCrashSegment(Map<String, Object> segments);
Handled Exceptions
@@ -195,17 +214,17 @@ config.setCustomCrashSegment(Map<String, Object> segments);
be provided to the function. A potential use case would be to
exception.toString()
-Countly.logException(String exception, bool nonfatal, [Map<String, Object> segmentation])
+Countly.logException(String exception, bool nonfatal, [Map<String, Object> segmentation])
The issue is recorded with a provided Exception object. If no stacktrace is set,StackTrace.current
will be used.
-Countly.logExceptionEx(Exception exception, bool nonfatal, {StackTrace stacktrace, Map<String, Object> segmentation})
+Countly.logExceptionEx(Exception exception, bool nonfatal, {StackTrace stacktrace, Map<String, Object> segmentation})
The exception/error is recorded through a string message. If no stack trace is
provided, StackTrace.current will be used.
-Countly.logExceptionManual(String message, bool nonfatal, {StackTrace stacktrace, Map<String, Object> segmentation})
+Countly.logExceptionManual(String message, bool nonfatal, {StackTrace stacktrace, Map<String, Object> segmentation})
Below are some examples that how to log handled/nonfatal and unhandled/fatal
exceptions manually.
@@ -213,7 +232,7 @@ config.setCustomCrashSegment(Map<String, Object> segments);
1. Manually report exception
-bool nonfatal = true; // Set it false in case of fatal exception
+bool nonfatal = true; // Set it false in case of fatal exception
// With Exception object
Countly.logExceptionEx(EXCEPTION_OBJECT, nonfatal);
@@ -223,7 +242,7 @@ Countly.logExceptionManual("MESSAGE_STRING", nonfatal);
2. Manually report exception with stack trace
-bool nonfatal = true; // Set it false in case of fatal exception
+bool nonfatal = true; // Set it false in case of fatal exception
// With Exception object
Countly.logExceptionEx(EXCEPTION_OBJECT, nonfatal, stacktrace: STACK_TRACE_OBJECT);
@@ -233,7 +252,7 @@ Countly.logExceptionManual("MESSAGE_STRING", nonfatal, stacktrace: STACK_TRACE_O
3. Manually report exception with segmentation
-bool nonfatal = true; // Set it false in case of fatal exception
+bool nonfatal = true; // Set it false in case of fatal exception
// With Exception object
Countly.logExceptionEx(EXCEPTION_OBJECT, nonfatal, segmentation: {"_facebook_version": "0.0.1"});
@@ -243,7 +262,7 @@ Countly.logExceptionManual("MESSAGE_STRING", nonfatal, segmentation: {"_facebook
4. Manually report exception with stack trace and segmentation
-bool nonfatal = true; // Set it false in case of fatal exception
+bool nonfatal = true; // Set it false in case of fatal exception
// With Exception object
Countly.logExceptionEx(EXCEPTION_OBJECT, nonfatal, STACK_TRACE_OBJECT, {"_facebook_version": "0.0.1"});
@@ -257,7 +276,7 @@ Countly.logExceptionManual("MESSAGE_STRING", nonfatal, STACK_TRACE_OBJECT, {"_fa
will be sent together with the crash report.
The following function call adds a crash breadcrumb:
-Countly.addCrashLog(String logs)
+Countly.addCrashLog(String logs)
Events
Event is any type of action that
@@ -272,7 +291,7 @@ Countly.logExceptionManual("MESSAGE_STRING", nonfatal, STACK_TRACE_OBJECT, {"_fa
In the SDK all event-related functionality can be browsed from the returned interface
on:
-Countly.instance.events
+Countly.instance.events
When providing segmentation for events, the following primitive data types are
supported: "String," "int," "double," and "bool." Additionally, Lists composed
@@ -285,43 +304,43 @@ Countly.logExceptionManual("MESSAGE_STRING", nonfatal, STACK_TRACE_OBJECT, {"_fa
of what information each usage will provide us:
- -
+
-
Usage 1: how many times a purchase event occurred.
- -
+
-
Usage 2: how many times a purchase event occurred + the
total amount of those purchases.
- -
+
-
Usage 3: how many times a purchase event occurred + which
countries and application versions those purchases were made from.
- -
+
-
Usage 4: how many times a purchase event occurred + the
total amount both of which are also available segmented into countries and
application versions.
- -
+
-
Usage 5: how many times purchase event occurred + the total
amount both of which are also available segmented into countries and application
versions + the total duration of those events (under Timed Events topic below).
The function signature as follows
-Future<String?> recordEvent(String key, [Map<String, Object>? segmentation, int? count, double? sum, int? duration])
+Future<String?> recordEvent(String key, [Map<String, Object>? segmentation, int? count, double? sum, int? duration])
1. Event key and count
-Countly.instance.events.recordEvent('purchase', null, 1);
+Countly.instance.events.recordEvent('purchase', null, 1);
2. Event key, count and sum
-Countly.instance.events.recordEvent('purchase', null, 1, 0.99);
+Countly.instance.events.recordEvent('purchase', null, 1, 0.99);
3. Event key and count with segmentation(s)
-Map<String, Object>? segmentation = {
+Map<String, Object>? segmentation = {
'country': 'Germany',
'app_version': '1.0',
'rating': 10,
@@ -337,7 +356,7 @@ Countly.instance.events.recordEvent('purchase', segmentation, 1);
4. Event key, count and sum with segmentation(s)
-Map<String, Object>? segmentation = {
+Map<String, Object>? segmentation = {
'country': 'Germany',
'app_version': '1.0',
'rating': 10,
@@ -353,7 +372,7 @@ Countly.instance.events.recordEvent('purchase', segmentation, 1, 0.99);
5. Event key, count, sum and duration with segmentation(s)
-Map<String, Object>? segmentation = {
+Map<String, Object>? segmentation = {
'country': 'Germany',
'app_version': '1.0',
'rating': 10,
@@ -370,7 +389,7 @@ Countly.instance.events.recordEvent('purchase', segmentation, 1, 0.99, 1);
It's possible to create timed events by defining a start and a stop moment.
-// Basic event
+// Basic event
Countly.instance.events.startEvent("Timed Event");
Timer timer = Timer(new Duration(seconds: 5), () {
@@ -378,9 +397,11 @@ Timer timer = Timer(new Duration(seconds: 5), () {
});
- You may also provide additional information when ending an event. However, in that case, you have to provide the segmentation, count, and sum. The default values for those are "null", 1 and 0.
+ You may also provide additional information when ending an event. However, in
+ that case, you have to provide the segmentation, count, and sum. The default
+ values for those are "null", 1 and 0.
-// Event with Segment, sum and count
+// Event with Segment, sum and count
Countly.instance.events.startEvent("Timed Event With Segment, Sum and Count");
Timer timer = Timer(new Duration(seconds: 5), () {
@@ -400,7 +421,7 @@ Timer timer = Timer(new Duration(seconds: 5), () {
You may cancel the started timed event in case it is not relevant anymore:
-//start some event
+//start some event
Countly.instace.events.startEvent(eventName);
//wait some time
@@ -418,16 +439,16 @@ Countly.instance.events.cancelEvent(eventName);
track a session automatically. This is how it works:
- -
+
-
Start/Begin session Request: It is sent to the server when
the app comes back to the foreground from the background, and it includes
basic metrics.
- -
+
-
Update Session Request: It automatically sends a periodical
(60 sec by default) update session request while the app is in the foreground.
- -
+
-
End Session Request: It is sent at the end of a session
when the app goes to the background or terminates.
@@ -444,22 +465,23 @@ Countly.instance.events.cancelEvent(eventName);
relying on the SDK.
It can be enabled during init with:
-
CountlyConfig config = CountlyConfig(SERVER_URL, APP_KEY);
-config.enableManualSessionHandling();
+CountlyConfig config = CountlyConfig(SERVER_URL, APP_KEY);
+
Afterwards it is up to the implementer to make calls to:
- - Begin session (Starts a session)
- -
+
- Begin session (Starts a session)
+ -
Update session duration (By default, you would call this every 60 seconds
after beginning a session so that it is not closed server side. If you would
- want to increase that duration, you would have to increase the "Maximal Session Duration" in your server API configuration)
+ want to increase that duration, you would have to increase the "Maximal Session
+ Duration" in your server API configuration)
- - End session (Ends and updates duration)
+ - End session (Ends and updates duration)
You can use the 'sessions interface' to make these calls:
-Countly.instance.sessions.beginSession();
+Countly.instance.sessions.beginSession();
Countly.instance.sessions.updateSession();
-Countly.instance.sessions.endSession();
+Countly.instance.sessions.endSession();
View Tracking
@@ -471,7 +493,7 @@ Countly.instance.sessions.endSession();
The SDK provides access to all view-related functionality through the interface
returned by:
-Countly.instance.views
+Countly.instance.views
Manual View Recording
You can manually track views in your application.
@@ -488,12 +510,12 @@ Countly.instance.sessions.endSession();
If you want to start a view that will be automatically stopped when starting
another view, use the following method:
-// record a view on your application
-final String? viewID = await Countly.instance.views.startAutoStoppedView("Dashboard");
+// record a view on your application
+final String? viewID = await Countly.instance.views.startAutoStoppedView("Dashboard");
You can also specify the custom segmentation key-value pairs while starting views:
-Map<String, Object> segmentation = {
+Map<String, Object> segmentation = {
"country": "Germany",
"app_version": "1.0",
"rating": 10,
@@ -504,21 +526,21 @@ final String? viewID = await Countly.instance.views.startAutoStoppedView
"sub_names": ["John", "Doe", "Jane"]
};
-final String? anotherViewID = Countly.instance.views.startAutoStoppedView("HomePage", segmentation);
+final String? anotherViewID = Countly.instance.views.startAutoStoppedView("HomePage", segmentation);
Regular Views
Opposed to "auto stopped views", with regular views you can have multiple of
them started at the same time, and then you can control them independently. You
- can manually start a view using the startViewmethod with a view name. This will start tracking a view and return a unique identifier, and the view will remain active until explicitly stopped using stopViewWithName or stopViewWithID
+ can manually start a view using the startViewmethod with a view name. This will start tracking a view and return a unique identifier, and the view will remain active until explicitly stopped using stopViewWithName or stopViewWithID
-// record a view on your application
+// record a view on your application
Countly.instance.views.startView("HomePage");
final String? viewID = await Countly.instance.views.startView("Dashboard");
You can also specify the custom segmentation key-value pairs while starting views:
-Map<String, Object> segmentation = {
+Map<String, Object> segmentation = {
"country": "Germany",
"app_version": "1.0",
"rating": 10,
@@ -537,30 +559,30 @@ final String? anotherViewID = Countly.instance.views.startView("HomePage", segme
you try to stop one with that name, the SDK would close one of those randomly.
Below you can see example ways of stopping views.
-Countly.instance.views.stopViewWithName("HomePage");
+Countly.instance.views.stopViewWithName("HomePage");
This function allows you to manually stop the tracking of a view identified by
its name. You can also specify the custom segmentation key-value pairs while stopping views:
-Countly.instance.views.stopViewWithName("HomePage", segmentation);
+Countly.instance.views.stopViewWithName("HomePage", segmentation);
You can also stop view tracking by its unique idetifier using
- stopViewWithID
+ stopViewWithID
-Countly.instance.views.stopViewWithID(viewID);
+Countly.instance.views.stopViewWithID(viewID);
You can also specify the custom segmentation key-value pairs while stopping views:
-Countly.instance.views.stopViewWithID(anotherViewID, segmentation);
+Countly.instance.views.stopViewWithID(anotherViewID, segmentation);
You can stop all views tracking using
- stopAllViews
+ stopAllViews
-Countly.instance.views.stopAllViews();
+Countly.instance.views.stopAllViews();
You can also specify the custom segmentation key-value pairs while stopping all views:
-Countly.instance.views.stopAllViews(segmentation);
+Countly.instance.views.stopAllViews(segmentation);
Pausing and Resuming Views
This SDK allows you to start multiple
@@ -571,18 +593,19 @@ final String? anotherViewID = Countly.instance.views.startView("HomePage", segme
You can pause view tracking by its unique identifier using
- pauseViewWithID
+ pauseViewWithID
-Countly.instance.views.pauseViewWithID(viewID);
+Countly.instance.views.pauseViewWithID(viewID);
- This function temporarily pauses the tracking of a view identified by its unique identifier.
+ This function temporarily pauses the tracking of a view identified by its unique
+ identifier.
- You can resume view tracking by its unique identifier using resumeViewWithID:
+ You can resume view tracking by its unique identifier using resumeViewWithID:
-Countly.instance.views.resumeViewWithID(viewID);
+Countly.instance.views.resumeViewWithID(viewID);
- This function resumes the tracking of a view identified by its unique identifier.
+ This function resumes the tracking of a view identified by its unique identifier.
Adding Segmentation to Started Views
@@ -599,7 +622,7 @@ final String? anotherViewID = Countly.instance.views.startView("HomePage", segme
Here is an example on how to achieve that using the view name:
-String viewName = 'HomePage';
+String viewName = 'HomePage';
await Countly.instance.views.startView(viewName);
Map<String, Object> segmentation = {
@@ -617,7 +640,7 @@ await Countly.instance.views.addSegmentationToViewWithName(viewName, segmentatio
Here is an example for how to add segmentation to a view using its ID:
-String? viewID = await Countly.instance.views.startView('HomePage');
+String? viewID = await Countly.instance.views.startView('HomePage');
Map<String, Object> segmentation = {
"country": "Germany",
@@ -642,7 +665,7 @@ await Countly.instance.views.addSegmentationToViewWithID(viewID!, segmentation);
For setting global segmentation values during SDK initialization, use the following
method:
-// set global segmentation at initialization
+// set global segmentation at initialization
final CountlyConfig config = CountlyConfig(SERVER_URL, APP_KEY);
config.setGlobalViewSegmentation(segmentation);
@@ -653,12 +676,12 @@ config.setGlobalViewSegmentation(segmentation);
ThesetGlobalViewSegmentation method will replace the previously
set values..
-Countly.instance.views.setGlobalViewSegmentation(segmentation);
+Countly.instance.views.setGlobalViewSegmentation(segmentation);
The updateGlobalViewSegmentation method will modify the previously
set values and overwrite any previously set keys.
-Countly.instance.views.updateGlobalViewSegmentation(segmentation);
+Countly.instance.views.updateGlobalViewSegmentation(segmentation);
Device ID Management
A device ID is a unique identifier for your users. You may specify the device
@@ -669,7 +692,7 @@ config.setGlobalViewSegmentation(segmentation);
You may provide your custom device ID when initializing the SDK:
-CountlyConfig config = CountlyConfig(SERVER_URL, APP_KEY);
+CountlyConfig config = CountlyConfig(SERVER_URL, APP_KEY);
config.setDeviceId(DEVICE_ID);
Changing the Device ID
@@ -680,7 +703,7 @@ config.setDeviceId(DEVICE_ID);
You may configure or change the device ID anytime using:
-Countly.instance.deviceId.setID(DEVICE_ID);
+Countly.instance.deviceId.setID(DEVICE_ID);
When using setID, the SDK determines internally if the device will
be counted as a new device on the server or if it will merge the new and old
@@ -706,7 +729,7 @@ config.setDeviceId(DEVICE_ID);
You can enable temporary device ID mode when initializing the SDK:
-CountlyConfig config = CountlyConfig(SERVER_URL, APP_KEY);
+CountlyConfig config = CountlyConfig(SERVER_URL, APP_KEY);
config.enableTemporaryDeviceIDMode();
// Initialize with that configuration
@@ -714,7 +737,7 @@ Countly.initWithConfig(config);
To enable a temporary device ID after initialization, you can call:
-Countly.instance.deviceId.enableTemporaryIDMode();
+Countly.instance.deviceId.enableTemporaryIDMode();
The SDK will be in temporary device ID mode, all requests will be on hold and
they will be persistently stored.
@@ -733,23 +756,20 @@ Countly.initWithConfig(config);
You may want to see what the current device ID is. For that, you can use the
following call:
-String? currentDeviceId = Countly.instance.deviceId.getID();
+String? currentDeviceId = Countly.instance.deviceId.getID();
- You can use getIDType method which returns a
- DeviceIDType to get the current device ID type. The ID type is an enum with the possible values of:
+ You can use getIDType method which returns a
+ DeviceIDType to get the current device ID type. The ID type is an
+ enum with the possible values of:
- -
- "DEVELOPER_SUPPLIED" - device ID was supplied by the host app.
-
- -
- "SDK_GENERATED" - device ID was generated by the SDK.
-
- -
- "TEMPORARY_ID" - the SDK is in temporary device ID mode.
+
-
+ "DEVELOPER_SUPPLIED" - device ID was supplied by the host app.
+ - "SDK_GENERATED" - device ID was generated by the SDK.
+ - "TEMPORARY_ID" - the SDK is in temporary device ID mode.
-DeviceIdType? deviceIdType = await Countly.instance.deviceId.getIDType();
+DeviceIdType? deviceIdType = await Countly.instance.deviceId.getIDType();
Device ID Generation
When the SDK is initialized for the first time with no device ID, it will generate
@@ -792,7 +812,7 @@ Countly.initWithConfig(config);
android/app/src/main/AndroidManifest.xml inside
application tag.
-<application ...>
+<application ...>
...
<service android:name="ly.count.dart.countly_flutter.CountlyMessagingService">
<intent-filter>
@@ -804,7 +824,7 @@ Countly.initWithConfig(config);
Step 6: Add the following line in file android/build.gradle
-buildscript {
+buildscript {
dependencies {
classpath 'com.google.gms:google-services:LATEST'
}
@@ -819,7 +839,7 @@ Countly.initWithConfig(config);
Step 7: Add the following line in file android/app/build.gradle
-dependencies {
+dependencies {
implementation 'ly.count.android:sdk:LATEST'
implementation 'com.google.firebase:firebase-messaging:LATEST'
}
@@ -851,15 +871,15 @@ apply plugin: 'com.google.gms.google-services'
For this purpose you can find CountlyNotificationService.h/m file
under:
-Pods/Development Pods/Countly/{PROJECT_NAME}/ios/.symlinks/plugins/countly_flutter/ios/Classes/CountlyiOS/CountlyNotificationService.h/m
+Pods/Development Pods/Countly/{PROJECT_NAME}/ios/.symlinks/plugins/countly_flutter/ios/Classes/CountlyiOS/CountlyNotificationService.h/m
Some tips to find the files from deep hierarchy:
- -
+
-
You can filter the files in the navigator using a shortcut ⌥⌘J (Option-Command-J),
in the filter box type "CountlyNotificationService" and it will show the
related files only.
- -
+
-
You can find the file using the shortcut ⇧⌘O (Shift-Command-O) and then navigate
to that file using the shortcut ⇧⌘J (Shift-Command-J)
@@ -874,18 +894,18 @@ apply plugin: 'com.google.gms.google-services'
token mode. This would allow you to choose either test or production modes, push
token mode should be set before init.
-
// Set messaging mode for push notifications
+// Set messaging mode for push notifications
Countly.pushTokenType(Countly.messagingMode["TEST"]);
When you are finally ready to initialise Countly push, you would call this:
-// This method will ask for permission, enables push notification and send push token to countly server.
+// This method will ask for permission, enables push notification and send push token to countly server.
Countly.askForNotificationPermission();
Also it is important to note that push notification is enabled for iOS by default,
so to disable you need to call disablePushNotifications method:
-// Disable push notifications feature for iOS, by default it is enabled.
+// Disable push notifications feature for iOS, by default it is enabled.
Countly.disablePushNotifications();
Removing Push and Its Dependencies
@@ -898,18 +918,18 @@ Countly.disablePushNotifications();
To register a Push Notification callback after initializing the SDK, use the
method below.
-Countly.onNotification((String notification) {
+Countly.onNotification((String notification) {
print(notification);
-});
+});
In order to listen to notification receive and click events, Place below code
in AppDelegate.swift
Add header files
-import countly_flutter
+import countly_flutter
Add these methods:
-// Required for the notification event. You must call the completion handler after handling the remote notification.
+// Required for the notification event. You must call the completion handler after handling the remote notification.
func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject], fetchCompletionHandler completionHandler: (UIBackgroundFetchResult) -> Void) {
CountlyFlutterPlugin.onNotification(userInfo);
completionHandler(.newData);
@@ -938,7 +958,7 @@ override func userNotificationCenter(\_ center: UNUserNotificationCenter, didRec
Here is the example of how data will receive in push callbacks:
Data Received for Android platform:
-{
+{
"c.e.cc": "TR",
"c.e.dt": "mobile",
"Key": "value",
@@ -950,9 +970,9 @@ override func userNotificationCenter(\_ center: UNUserNotificationCenter, didRec
"sound": "custom",
"title": "title",
"message": "Message"
-}
+}
Data Received for iOS platform:
-{
+{
Key = value;
aps = {
alert = {
@@ -973,7 +993,7 @@ override func userNotificationCenter(\_ center: UNUserNotificationCenter, didRec
i = 62b5b945cabedb0870e9f217;
l = "https://www.google.com/";
};
-}
+}
User Location
@@ -996,23 +1016,21 @@ override func userNotificationCenter(\_ center: UNUserNotificationCenter, didRec
When setting user location information, you would be setting these values:
- -
+
-
countryCode a string in ISO 3166-1 alpha-2 format country code
- -
+
-
city a string specifying city name
- -
+
-
location a string comma-separated latitude and longitude
- -
+
-
IP a string specifying an IP address in IPv4 or IPv6 formats
-
- All values are optional, but at least one should be set.
-
-// Example for setLocation
+All values are optional, but at least one should be set.
+// Example for setLocation
CountlyConfig config = CountlyConfig(SERVER_URL, APP_KEY);
config.setLocation(country_code: 'TR', city: 'Istanbul', gpsCoordinates: '41.0082,28.9784', ipAddress: '10.2.33.12')
@@ -1020,7 +1038,7 @@ config.setLocation(country_code: 'TR', city: 'Istanbul', gpsCoordinates: '41.008
SDK has started. To do so, use the setUserLocation method as shown
below.
-// Example for setUserLocation
+// Example for setUserLocation
Countly.setUserLocation(countryCode: 'TR', city: 'Istanbul', gpsCoordinates: '41.0082,28.9784', ipAddress: '10.2.33.12');
Disable Location
@@ -1030,7 +1048,7 @@ Countly.setUserLocation(countryCode: 'TR', city: 'Istanbul', gpsCoordinates: '41
setUserLocation is called with any non-null value, tracking will
resume.
-//disable location tracking
+//disable location tracking
Countly.disableLocation();
Remote Config
@@ -1050,9 +1068,7 @@ Countly.disableLocation();
stay as they were. A previously valid key may return no value after a full download.
-
- Downloading Values
-
+ Downloading Values
Automatic Remote Config Triggers
@@ -1067,17 +1083,17 @@ Countly.disableLocation();
The automatic download triggers that would trigger a full value download are:
- -
+
-
when the SDK has finished initializing
- -
+
-
after the device ID is changed without merging
- -
+
-
when user gets out of temp ID mode
- -
- when
CountlyConsent.remoteConfig consent is given after it had been removed before (if consents are enabled)
+ -
+ when
CountlyConsent.remoteConfig consent is given after it had been removed before (if consents are enabled)
@@ -1085,7 +1101,7 @@ Countly.disableLocation();
enableRemoteConfigAutomaticTriggers on the configuration
object you will provide during init.
- CountlyConfig config = CountlyConfig(SERVER_URL, APP_KEY)
+ CountlyConfig config = CountlyConfig(SERVER_URL, APP_KEY)
..enableRemoteConfigAutomaticTriggers(); // necessary to enable the feature
@@ -1094,33 +1110,33 @@ Countly.disableLocation();
were not updated, you would have metadata indicating if a value belongs to
the old or current user.
- CountlyConfig config = CountlyConfig(SERVER_URL, APP_KEY)
- ..enableRemoteConfigValueCaching();
+ CountlyConfig config = CountlyConfig(SERVER_URL, APP_KEY)
+ ..enableRemoteConfigValueCaching();
Manually Calls
There are three ways to trigger remote config value download manually:
- -
+
-
Manually downloading all keys
- -
+
-
Manually downloading specific keys
- - Manually downloading, omitting (everything except) keys.
+ - Manually downloading, omitting (everything except) keys.
Each of these calls also has an optional parameter that you can provide a RCDownloadCallback to, which would be triggered when the download attempt has finished.
- dowloadAllKeys is the same as the automatically triggered update - it replaces all stored values with the ones from the server (all locally stored values are deleted and replaced with new ones).
+ dowloadAllKeys is the same as the automatically triggered update - it replaces all stored values with the ones from the server (all locally stored values are deleted and replaced with new ones).
- Or you might only want to update specific key values. To do so, you will need to call downloadSpecificKeys to downloads new values for the wanted keys. Those are provided with a String array.
+ Or you might only want to update specific key values. To do so, you will need to call downloadSpecificKeys to downloads new values for the wanted keys. Those are provided with a String array.
- Or you might want to update all the values except a few defined keys. To do so, call downloadOmittingKeys would update all values except the provided keys. The keys are provided with a String array.
+ Or you might want to update all the values except a few defined keys. To do so, call downloadOmittingKeys would update all values except the provided keys. The keys are provided with a String array.
- Countly.instance.remoteConfig.downloadAllKeys((rResult, error, fullValueUpdate, downloadedValues) {
+ Countly.instance.remoteConfig.downloadAllKeys((rResult, error, fullValueUpdate, downloadedValues) {
if (rResult == RequestResult.Success) {
// do sth
} else {
@@ -1138,7 +1154,7 @@ Countly.disableLocation();
});
- Countly.instance.remoteConfig.downloadSpecificKeys(List<String> keysToInclude, (rResult, error, fullValueUpdate, downloadedValues) {
+ Countly.instance.remoteConfig.downloadSpecificKeys(List<String> keysToInclude, (rResult, error, fullValueUpdate, downloadedValues) {
if (rResult == RequestResult.Success) {
// do sth
} else {
@@ -1147,7 +1163,7 @@ Countly.disableLocation();
});
- Countly.instance.remoteConfig.downloadOmittingKeys(List<String> keysToExclude, (rResult, error, fullValueUpdate, downloadedValues) {
+ Countly.instance.remoteConfig.downloadOmittingKeys(List<String> keysToExclude, (rResult, error, fullValueUpdate, downloadedValues) {
if (rResult == RequestResult.Success) {
// do sth
} else {
@@ -1157,7 +1173,7 @@ Countly.disableLocation();
- When making requests with an "inclusion" or "exclusion" array, if those arrays are empty or null, they will function the same as a dowloadAllKeys request and will update all the values. This means it will also erase all keys not returned by the server.
+ When making requests with an "inclusion" or "exclusion" array, if those arrays are empty or null, they will function the same as a dowloadAllKeys request and will update all the values. This means it will also erase all keys not returned by the server.
Accessing Values
@@ -1165,10 +1181,10 @@ Countly.disableLocation();
key. This returns an Future<RCData> object that contains the value of the
key and the metadata about that value's owner. If value in RCData was
null
- then no value was found or the value was null.
+ then no value was found or the value was null.
-Object? value_1 = await Countly.instance.remoteConfig.getValue("key_1").value;
+Object? value_1 = await Countly.instance.remoteConfig.getValue("key_1").value;
Object? value_2 = await Countly.instance.remoteConfig.getValue("key_2").value;
Object? value_3 = await Countly.instance.remoteConfig.getValue("key_3").value;
Object? value_4 = await Countly.instance.remoteConfig.getValue("key_4").value;
@@ -1182,10 +1198,10 @@ JSONObject jObj = value4 as JSONObject;
If you want to get all values together you can use
getAllValues which returns a Future<Map<String,
RCData>>.
- The SDK does not know the returned value type, so, it will return the Object. The developer then needs to cast it to the appropriate type. The returned values may also be JSONArray, JSONObject,
+ The SDK does not know the returned value type, so, it will return the Object. The developer then needs to cast it to the appropriate type. The returned values may also be JSONArray, JSONObject,
or just a simple value, such as int.
-Map<String, RCData> allValues = await Countly.instance.remoteConfig.getAllValues();
+Map<String, RCData> allValues = await Countly.instance.remoteConfig.getAllValues();
int intValue = allValues["key_1"] as int;
double doubleValue = allValues["key_2"] as double;
@@ -1197,7 +1213,7 @@ JSONObject jObj = allValues["key_4"] as JSONObject;
belongs to. The isCurrentUsersData is only false when there was a device ID change,
but somehow (or intentionally) a remote config value was not updated.
-Class RCData {
+Class RCData {
Object value;
Boolean isCurrentUsersData;
}
@@ -1211,14 +1227,14 @@ JSONObject jObj = allValues["key_4"] as JSONObject;
At some point, you might like to erase all the values downloaded from the server. You will need to call one function to do so.
-Countly.instance.remoteConfig.clearAll();
+Countly.instance.remoteConfig.clearAll();
Global Download Callbacks
Also, you may provide a global callback function to be informed when the remote
config download request is finished with
remoteConfigRegisterGlobalCallback during the SDK initialization:
-CountlyConfig config = CountlyConfig(SERVER_URL, APP_KEY)
+CountlyConfig config = CountlyConfig(SERVER_URL, APP_KEY)
..remoteConfigRegisterGlobalCallback((rResult, error, fullValueUpdate, downloadedValues) {
if (error != null) {
// do sth
@@ -1230,24 +1246,24 @@ JSONObject jObj = allValues["key_4"] as JSONObject;
and it would have the following parameters:
- -
+
-
rResult: RequestResult Enum (either
- Error, Success or NetworkIssue)
+ Error, Success or NetworkIssue)
- -
+
-
error: String (error message. "null" if there is
no error)
- -
+
-
fullValueUpdate: boolean ("true" - all values updated,
"false" - a subset of values updated)
- -
+
-
downloadedValues: Map<String, RCData> (the
whole downloaded remote config values)
-RCDownloadCallback {
+RCDownloadCallback {
void callback(RequestResult rResult, String error, boolean fullValueUpdate, Map<String, RCData> downloadedValues)
}
@@ -1261,7 +1277,7 @@ JSONObject jObj = allValues["key_4"] as JSONObject;
You can also register (or remove) callbacks to do different things after the
SDK initialization. You can register these callbacks multiple times:
-// register a callback
+// register a callback
Countly.instance.remoteConfig.registerDownloadCallback((rResult, error, fullValueUpdate, downloadedValues) {
// do sth
});
@@ -1276,36 +1292,34 @@ Countly.instance.remoteConfig.removeDownloadCallback((rResult, error, fullValueU
from some or all existing A/B tests available.
-
- Enrollment on Download
-
+ Enrollment on Download
You can enroll into the A/B tests automatically whenever you download RC
values from the server. To do so you have to set the following flag at the
config object during initialization:
- CountlyConfig config = CountlyConfig(SERVER_URL, APP_KEY)
-..enrollABOnRCDownload();
-
- Enrollment on Access
-
+ CountlyConfig config = CountlyConfig(SERVER_URL, APP_KEY)
+..enrollABOnRCDownload();
+ Enrollment on Access
- You can also enroll to A/B tests while getting RC values from storage. You can use getValueAndEnroll while getting a single value and getAllValuesAndEnroll while getting all values to enroll to the keys that exist. If no value was stored for those keys these functions would not enroll the user. Both of these functions works the same way with their non-enrolling variants, namely; getValue and getAllValues.
+ You can also enroll to A/B tests while getting RC values from storage. You
+ can use getValueAndEnroll while getting a single value and
+ getAllValuesAndEnroll while getting all values to enroll to
+ the keys that exist. If no value was stored for those keys these functions
+ would not enroll the user. Both of these functions works the same way with
+ their non-enrolling variants, namely; getValue and
+ getAllValues.
-
- Enrollment on Action
-
+ Enrollment on Action
To enroll a user into the A/B tests for the given keys you use the following
method:
- Countly.instance.remoteConfig.enrollIntoABTestsForKeys(List<String> keys);
+ Countly.instance.remoteConfig.enrollIntoABTestsForKeys(List<String> keys);
Here the keys array is the mandatory parameter for this method to work.
-
- Exiting A/B Tests
-
+ Exiting A/B Tests
Platform Info
@@ -1317,7 +1331,7 @@ Countly.instance.remoteConfig.removeDownloadCallback((rResult, error, fullValueU
If you want to remove users from A/B tests of certain keys you can use the following
function:
-Countly.instance.remoteConfig.exitABTestsForKeys(List<String> keys);
+Countly.instance.remoteConfig.exitABTestsForKeys(List<String> keys);
Here if no keys are provided it would remove the user from all A/B tests instead.
@@ -1349,12 +1363,12 @@ Countly.instance.remoteConfig.removeDownloadCallback((rResult, error, fullValueU
dashboard. If the user dismisses star rating dialog without giving a rating,
the event will not be recorded.
-Countly.askForStarRating();
+Countly.askForStarRating();
The star-rating dialog's title, message, and dismiss button text may be customized
through the following functions:
-CountlyConfig config = CountlyConfig(SERVER_URL, APP_KEY);
+CountlyConfig config = CountlyConfig(SERVER_URL, APP_KEY);
config.setStarRatingTextTitle("Custom title"); // Only available for Android
config.setStarRatingTextMessage("Custom message");
config.setStarRatingTextDismiss("Custom message"); // Only available for Android
@@ -1368,10 +1382,10 @@ config.setStarRatingTextDismiss("Custom message"); // Only available for Android
It is possible to display 3 kinds of feedback widgets:
- NPS,
- Survey,
+ NPS,
+ Survey,
and
- Rating.
+ Rating.
For more detailed information about Feedback Widgets, you can refer to
@@ -1387,12 +1401,12 @@ config.setStarRatingTextDismiss("Custom message"); // Only available for Android
After you have created widgets on your dashboard, you can reach the methods to
show them from the feedback interface of your Countly instance:
-Countly.instance.feedback
+Countly.instance.feedback
You can display a random active widget for the widget type you want with one
of these methods:
-.presentNPS([String? nameIDorTag, FeedbackCallback? feedbackCallback])
+.presentNPS([String? nameIDorTag, FeedbackCallback? feedbackCallback])
.presentRating([String? nameIDorTag, FeedbackCallback? feedbackCallback])
.presentSurvey([String? nameIDorTag, FeedbackCallback? feedbackCallback])
@@ -1420,7 +1434,7 @@ Countly.instance.feedback.presentNPS("MyNetPromoterScore", FeedbackCallback(
For more in-depth information on retrieving feedback widgets, understanding object
structures, or presenting them yourself, please refer to the following
- resource.
+ resource.
Manual Reporting
@@ -1445,7 +1459,7 @@ Countly.instance.feedback.presentNPS("MyNetPromoterScore", FeedbackCallback(
In case you want to use with callback then you can call 'getFeedbackWidgetData'
in this way:
-Countly.getFeedbackWidgetData(chosenWidget, onFinished: (retrievedWidgetData, error) {
+Countly.getFeedbackWidgetData(chosenWidget, onFinished: (retrievedWidgetData, error) {
if (error == null) {
}
});
@@ -1453,7 +1467,7 @@ Countly.instance.feedback.presentNPS("MyNetPromoterScore", FeedbackCallback(
If you want to use it without a callback then you can call 'getFeedbackWidgetData'
in this way:
-List result = await Countly.getFeedbackWidgetData(chosenWidget);
+List result = await Countly.getFeedbackWidgetData(chosenWidget);
String? error = result[1];
if (error == null) {
Map<String, dynamic> retrievedWidgetData = result[0];
@@ -1468,7 +1482,7 @@ if (error == null) {
use it, the widgetInformation and the widgetData to report the feedback result
with the following call:
-//this contains the reported results
+//this contains the reported results
Map<String, Object> reportedResult = {};
//
@@ -1523,25 +1537,25 @@ Countly.reportFeedbackWidgetManually(chosenWidget, retrievedWidgetData , reporte
Using the following call, you can set both the predefined and the custom user
properties during initialization:
-var userProperties = {
+var userProperties = {
"customProperty": "custom Value",
"username": "USER_NAME",
"email": "USER_EMAIL"
};
CountlyConfig config = CountlyConfig(SERVER_URL, APP_KEY);
config.setUserProperties(userProperties);
-Setting User profile values
+Setting User profile values
The following calls can be used after init.
If you want to set a single property, you can call
Countly.instance.userProfile.setProperty(key, value)
-Countly.instance.userProfile.setProperty("specialProperty", "value");
+Countly.instance.userProfile.setProperty("specialProperty", "value");
If you want to set multiple properties at the same time, you can use:
Countly.instance.userProfile.setUserProperties(userProperties)
-// example for setting user data
+// example for setting user data
Map<String, Object> userProperties= {
"name": "Nicola Tesla",
"username": "nicola",
@@ -1574,7 +1588,7 @@ Countly.instance.userProfile.setUserProperties(userProperties);
the same property.
Below is the list of available methods:
-//increment used value by 1
+//increment used value by 1
Countly.instance.userProfile.increment("increment");
//increment used value by provided value
Countly.instance.userProfile.incrementBy("incrementBy", 10);
@@ -1606,7 +1620,7 @@ Countly.instance.userProfile.pull("type", "morning");
to start using them you would first need to enable them and give the required
consent if it was required:
-CountlyConfig config = CountlyConfig(SERVER_URL, APP_KEY);
+CountlyConfig config = CountlyConfig(SERVER_URL, APP_KEY);
// this interface exposes the available APM features and their modifications.
config.apm.
@@ -1625,9 +1639,9 @@ config.apm.
with the same key.
To start a custom trace, use:
-Countly.startTrace(traceKey);
+Countly.startTrace(traceKey);
To end a custom trace, use:
-String traceKey = "Trace Key";
+String traceKey = "Trace Key";
Map<String, int> customMetric = {
"ABC": 1233,
"C44C": 1337
@@ -1643,7 +1657,7 @@ Countly.endTrace(traceKey, customMetric);
for your selected approach of making network requests and then call this after
your network request is done:
-Countly.recordNetworkTrace(networkTraceKey, responseCode, requestPayloadSize, responsePayloadSize, startTime, endTime);
+Countly.recordNetworkTrace(networkTraceKey, responseCode, requestPayloadSize, responsePayloadSize, startTime, endTime);
networkTraceKey is a unique identifier of the API endpoint you are
targeting or just the url you are targeting, all params should be stripped. You
@@ -1657,8 +1671,8 @@ Countly.endTrace(traceKey, customMetric);
These are:
- - App Start Time
- - App Background and Foreground time
+ - App Start Time
+ - App Background and Foreground time
Tracking of these metrics are disabled by default and must be explicitly enabled
@@ -1668,10 +1682,10 @@ Countly.endTrace(traceKey, customMetric);
For tracking app start time automatically you will need to enable it in SDK init
config:
-CountlyConfig config = CountlyConfig(SERVER_URL, APP_KEY);
+CountlyConfig config = CountlyConfig(SERVER_URL, APP_KEY);
// enable it here separately with 'apm' interface.
-config.apm.enableAppStartTimeTracking();
+config.apm.enableAppStartTimeTracking();
This calculates and records the app launch time for performance monitoring.
@@ -1680,10 +1694,10 @@ config.apm.enableAppStartTimeTracking();
will have to enable the usage of manual triggers together with
enableAppStartTimeTracking during init:
-CountlyConfig config = CountlyConfig(SERVER_URL, APP_KEY);
+CountlyConfig config = CountlyConfig(SERVER_URL, APP_KEY);
// enable it here separately with 'apm' interface.
-config.apm.enableAppStartTimeTracking().enableManualAppLoadedTrigger();
+config.apm.enableAppStartTimeTracking().enableManualAppLoadedTrigger();
Now you can call Countly.appLoadingFinished() any time
after SDK initialization to record that moment as the end of app launch time.
@@ -1696,21 +1710,21 @@ config.apm.enableAppStartTimeTracking().enableManualAppLoadedTrigger
-CountlyConfig config = CountlyConfig(SERVER_URL, APP_KEY);
+CountlyConfig config = CountlyConfig(SERVER_URL, APP_KEY);
// generate the timestamp you want (or you can directly pass a ts)
int ts = DateTime.now().millisecondsSinceEpoch - 500; // 500 ms ago as an example
// this would also work with manual trigger
-config.apm.enableAppStartTimeTracking().setAppStartTimestampOverride(ts);
+config.apm.enableAppStartTimeTracking().setAppStartTimestampOverride(ts);
Lastly if you want to enable the SDK to record the time an app is in foreground
or background automatically you would need to enable this option during init:
-CountlyConfig config = CountlyConfig(SERVER_URL, APP_KEY);
+CountlyConfig config = CountlyConfig(SERVER_URL, APP_KEY);
// enable it here separately with 'apm' interface.
-config.apm.enableForegroundBackgroundTracking();
+config.apm.enableForegroundBackgroundTracking();
User Consent
For compatibility with data protection regulations, such as GDPR, the Countly
@@ -1724,26 +1738,26 @@ config.apm.enableForegroundBackgroundTracking();
Currently, available features with consent control are as follows:
- -
+
-
sessions - tracking when, how often and how long users use your app.
- - events - allow sending events to the server.
- - views - allow tracking which views user visits.
- - location - allow sending location information.
- - crashes - allow tracking crashes, exceptions and errors.
- -
+
- events - allow sending events to the server.
+ - views - allow tracking which views user visits.
+ - location - allow sending location information.
+ - crashes - allow tracking crashes, exceptions and errors.
+ -
attribution - allow tracking from which campaign did user come.
- -
+
-
users - allow collecting/providing user information, including custom properties.
- - push - allow push notifications
- - starRating - allow sending their rating and feedback
- - apm - allow application performance monitoring
- -
+
- push - allow push notifications
+ - starRating - allow sending their rating and feedback
+ - apm - allow application performance monitoring
+ -
remoteConfig - allows downloading remote config values from your server
- -
+
-
content - allow to enter content zone to receive contents from the server.
@@ -1752,7 +1766,7 @@ config.apm.enableForegroundBackgroundTracking();
By default the requirement for consent is disabled. To enable it, you have to
call setRequiresConsent with true, before initializing Countly.
-CountlyConfig config = CountlyConfig(SERVER_URL, APP_KEY);
+CountlyConfig config = CountlyConfig(SERVER_URL, APP_KEY);
config.setRequiresConsent(true);
By default, no consent is given. That means that if no consent is enabled, Countly
@@ -1766,8 +1780,9 @@ config.setRequiresConsent(true);
of consent values. Or, you can use giveAllConsentsfor
all consent values.
-CountlyConfig config = CountlyConfig(SERVER_URL, APP_KEY);
-config.setConsentEnabled([CountlyConsent.location, CountlyConsent.sessions, CountlyConsent.attribution, CountlyConsent.push, CountlyConsent.events, CountlyConsent.views, CountlyConsent.crashes, CountlyConsent.users, CountlyConsent.push, CountlyConsent.starRating, CountlyConsent.apm, CountlyConsent.feedback, CountlyConsent.remoteConfig, CountlyConsent.content])
config.giveAllConsents()
+CountlyConfig config = CountlyConfig(SERVER_URL, APP_KEY);
+config.setConsentEnabled([CountlyConsent.location, CountlyConsent.sessions, CountlyConsent.attribution, CountlyConsent.push, CountlyConsent.events, CountlyConsent.views, CountlyConsent.crashes, CountlyConsent.users, CountlyConsent.push, CountlyConsent.starRating, CountlyConsent.apm, CountlyConsent.feedback, CountlyConsent.remoteConfig, CountlyConsent.content])
+config.giveAllConsents()
The Countly SDK does not persistently store the status of given consents except
push notifications. You are expected to handle receiving consent from end-users
@@ -1785,7 +1800,7 @@ config.setConsentEnabled([CountlyConsent.location, CountlyConsent.sessions, Coun
To reflect these changes in the Countly SDK, you can use the
removeConsent or giveConsent methods.
-//give consent values after init
+//give consent values after init
Countly.giveConsent([CountlyConsent.events, CountlyConsent.views, CountlyConsent.starRating, CountlyConsent.crashes]);
//remove consent values after init
@@ -1794,7 +1809,7 @@ Countly.removeConsent([CountlyConsent.events, CountlyConsent.views, CountlyConse
You can also either give or remove consent to all possible SDK features:
-//give consent to all features
+//give consent to all features
Countly.giveAllConsent();
//remove consent from all features
@@ -1809,7 +1824,7 @@ Countly.removeAllConsent();
is set, all requests would be checked for the validity of
&checksum field before being processed.
-// sending data with salt
+// sending data with salt
CountlyConfig config = CountlyConfig(SERVER_URL, APP_KEY);
config.setParameterTamperingProtectionSalt("salt");
@@ -1827,70 +1842,81 @@ config.setParameterTamperingProtectionSalt("salt");
Here is the list of functionalities "CountlyConfig" provides:
- -
- Device Id - A device ID is a unique identifier for your users. You may specify the device ID yourself or allow the SDK to generate it.
+
-
+ Device Id - A
+ device ID is a unique identifier for your users. You may specify the device
+ ID yourself or allow the SDK to generate it.
- -
- Enable Logging -
- To enable countly internal debugging logs.
+
-
+ Enable Logging -
+ To enable countly internal debugging logs.
- -
- Enable Crash Reporting -
+
-
+ Enable Crash Reporting -
To enable uncaught crash reporting.
- -
- Salt -
+
-
+ Salt -
Set the optional salt to be used for calculating the checksum of requested
- data which will be sent with each request.
+ data which will be sent with each request.
- -
- Event queue threshold -
+
-
+ Event queue threshold -
Set the threshold for event grouping. Event count that is bellow the threshold
- will be sent on update ticks.
+ will be sent on update ticks.
- -
+
-
Update Session Timer - Sets the interval for the automatic
session update calls.
- -
- Custom Crash Segment -Set
+
-
+ Custom Crash Segment -Set
custom crash segmentation which will be added to all recorded crashes.
- -
+
-
User consent
- Set if consent should be required and give consents.
- -
- Forcing HTTP POST - When set to true, all requests made to the Countly server will be done using HTTP POST. Otherwise, the SDK sends all requests using the HTTP GET method. In some cases, if the data to be sent exceeds the 1800-character limit, the SDK uses the POST method. The default value is false.
+
-
+ Forcing HTTP POST - When
+ set to true, all requests made to the Countly server
+ will be done using HTTP POST. Otherwise, the SDK sends all requests using
+ the HTTP GET method. In some cases, if the data to be sent exceeds the 1800-character
+ limit, the SDK uses the POST method. The default value is false.
- -
- Star Rating Text - Set shown title, message and dismiss buttim text for the star rating dialogs.
+
-
+ Star Rating Text -
+ Set shown title, message and dismiss buttim text for the star rating dialogs.
(no Web platform support)
- -
- Application Performance Monitoring - Enable APM features, which includes the recording of app start time.
+
-
+ Application Performance Monitoring -
+ Enable APM features, which includes the recording of app start time.
(no Web platform support)
- -
- Set User Location - Set user location manually instead of using Countly server to use GeoIP database to deduce a user's location.
+
-
+ Set User Location -
+ Set user location manually instead of using Countly server to use GeoIP database
+ to deduce a user's location.
- -
- Max Queue Size Limit - Set maximum size for the request queue.
+
-
+ Max Queue Size Limit - Set
+ maximum size for the request queue.
- -
- Manual Sessions - To enable manual session handling
- (no Web platform support)
+
-
+ Manual Sessions -
+ To enable manual session handling (no Web platform support)
- -
- Automatic Remote Config - If
+
-
+ Automatic Remote Config - If
enabled, will automatically download newest remote config values.
- -
- Direct Attribution -
+
-
+ Direct Attribution -
Report direct user attribution (no Web platform support)
- -
- Indirect Attribution -
+
-
+ Indirect Attribution -
Report indirect user attribution (no Web platform support)
@@ -1900,7 +1926,7 @@ config.setParameterTamperingProtectionSalt("salt");
example
application. It assumes Flutter is installed in your system:
-# clone the Countly SDK repository
+# clone the Countly SDK repository
git clone https://github.com/Countly/countly-sdk-flutter-bridge.git
# dive into the cloned repo
@@ -1939,7 +1965,7 @@ flutter run
If you do not specify a value for the setMaxRequestQueueSize flag, the default
setting of 1,000 will be used.
-CountlyConfig config = CountlyConfig(SERVER_URL, APP_KEY);
+CountlyConfig config = CountlyConfig(SERVER_URL, APP_KEY);
config.setMaxRequestQueueSize(5000);
SDK Internal Limits
@@ -1953,7 +1979,7 @@ config.setMaxRequestQueueSize(5000);
Limits the maximum size of all user set keys (default: 128 chars):
-CountlyConfig config = CountlyConfig(SERVER_URL, APP_KEY);
+CountlyConfig config = CountlyConfig(SERVER_URL, APP_KEY);
config.sdkInternalLimits.setMaxKeyLength(int MAX_KEY_LENGTH);
await Countly.initWithConfig(config);
Value Size
@@ -1961,14 +1987,14 @@ await Countly.initWithConfig(config);
Limits the size of all user set string segmentation (or their equivalent) values
(default: 256 chars):
-CountlyConfig config = CountlyConfig(SERVER_URL, APP_KEY);
+CountlyConfig config = CountlyConfig(SERVER_URL, APP_KEY);
config.sdkInternalLimits.setMaxValueSize(int MAX_VALUE_SIZE);
await Countly.initWithConfig(config);
Segmentation Values
Limits the amount of user set segmentation key-value pairs (default: 100 entries):
-CountlyConfig config = CountlyConfig(SERVER_URL, APP_KEY);
+CountlyConfig config = CountlyConfig(SERVER_URL, APP_KEY);
config.sdkInternalLimits.setMaxSegmentationValues(int MAX_SEGMENTATION_COUNT);
await Countly.initWithConfig(config);
Breadcrumb Count
@@ -1976,21 +2002,21 @@ await Countly.initWithConfig(config);
Limits the amount of user set breadcrumbs that can be recorded (default: 100
entries, exceeding this deletes the oldest one):
-CountlyConfig config = CountlyConfig(SERVER_URL, APP_KEY);
+CountlyConfig config = CountlyConfig(SERVER_URL, APP_KEY);
config.sdkInternalLimits.setMaxBreadcrumbCount(int MAX_BREADCRUMB_COUNT);
await Countly.initWithConfig(config);
Stack Trace Lines Per Thread
Limits the stack trace lines that would be recorded per thread (default: 30 lines):
-CountlyConfig config = CountlyConfig(SERVER_URL, APP_KEY);
+CountlyConfig config = CountlyConfig(SERVER_URL, APP_KEY);
config.sdkInternalLimits.setMaxStackTraceLinesPerThread(int MAX_STACK_THREAD);
await Countly.initWithConfig(config);
Stack Trace Line Length
Limits the characters that are allowed per stack trace line (default: 200 chars):
-CountlyConfig config = CountlyConfig(SERVER_URL, APP_KEY);
+CountlyConfig config = CountlyConfig(SERVER_URL, APP_KEY);
config.sdkInternalLimits.setMaxStackTraceLineLength(int MAX_STACK_LENGTH);
await Countly.initWithConfig(config);
Attribution
@@ -2006,11 +2032,9 @@ await Countly.initWithConfig(config);
from specific campaigns. This feature is available for the Enterprise Edition.
- There are 2 forms of attribution: direct Attribution and indirect Attribution.
+ There are 2 forms of attribution: direct Attribution and indirect Attribution.
-
- Direct Attribution
-
+Direct Attribution
Platform Info
@@ -2024,16 +2048,17 @@ await Countly.initWithConfig(config);
object.
- You can use recordDirectAttribution to set attribution values during initialization.
+ You can use recordDirectAttribution to set attribution values during
+ initialization.
-String campaignData = 'JSON_STRING';
+String campaignData = 'JSON_STRING';
CountlyConfig config = CountlyConfig(SERVER_URL, APP_KEY);
config.recordDirectAttribution('CAMPAIN_TYPE', campaignData);
You can also use recordDirectAttribution function to manually report
attribution later:
-String campaignData = 'JSON_STRING';
+String campaignData = 'JSON_STRING';
Countly.recordDirectAttribution('CAMPAIN_TYPE', campaignData);
Currently this feature is limited and accepts data only in a specific format
@@ -2041,20 +2066,19 @@ Countly.recordDirectAttribution('CAMPAIN_TYPE', campaignData);
attribution. The data also needs to be formatted in a specific way. Either with
the campaign id or with the campaign id and campaign user id.
-String campaignData = '{cid:"[PROVIDED_CAMPAIGN_ID]", cuid:"[PROVIDED_CAMPAIGN_USER_ID]"}';
+String campaignData = '{cid:"[PROVIDED_CAMPAIGN_ID]", cuid:"[PROVIDED_CAMPAIGN_USER_ID]"}';
Countly.recordDirectAttribution('countly', campaignData);
-
- Indirect Attribution
-
+Indirect Attribution
This feature would be used to report things like advertising ID's. For each platform
those would be different values. For the most popular keys we have a class with
predefined values to use, it is called "AttributionKey".
- You can use recordDirectAttribution to set attribution values during initialization.
+ You can use recordDirectAttribution to set attribution values during
+ initialization.
-Map<String, String> attributionValues = {};
+Map<String, String> attributionValues = {};
if(Platform.isIOS){
attributionValues[AttributionKey.IDFA] = 'IDFA';
}
@@ -2063,12 +2087,12 @@ else {
}
CountlyConfig config = CountlyConfig(SERVER_URL, APP_KEY);
-config.recordIndirectAttribution(attributionValues);
+config.recordIndirectAttribution(attributionValues);
You can also use recordIndirectAttribution function to manually
report attribution later
-Map<String, String> attributionValues = {};
+Map<String, String> attributionValues = {};
if(Platform.isIOS){
attributionValues[AttributionKey.IDFA] = 'IDFA';
}
@@ -2090,20 +2114,14 @@ Countly.recordIndirectAttribution(attributionValues);
can use the same function later in the app's life cycle to disable the override.
This function has to be called every time the app starts.
-CountlyConfig config = CountlyConfig(SERVER_URL, APP_KEY);
+CountlyConfig config = CountlyConfig(SERVER_URL, APP_KEY);
config.setHttpPostForced(true); // default is false
Setting Custom Network Request Headers
-
-
- Platform Info
- This feature is not supported in the Web platform.
-
-
If you need to include custom network request headers in the requests sent by
the SDK, you can easily add them using the following method.
-CountlyConfig config = CountlyConfig(SERVER_URL, APP_KEY);
+CountlyConfig config = CountlyConfig(SERVER_URL, APP_KEY);
config.setCustomNetworkRequestHeaders({'customHeaderKey': 'customHeaderValue'});
This allows you to specify any headers your application requires for enhanced
@@ -2122,7 +2140,7 @@ config.setCustomNetworkRequestHeaders({'customHeaderKey': 'customHeaderValue'});
1. You can replace all requests with a different app key with the current app
key:
-//Replaces all requests with a different app key with the current app key.
+//Replaces all requests with a different app key with the current app key.
Countly.replaceAllAppKeysInQueueWithCurrentAppKey();
In the request queue, if there are any requests whose app key is different than
@@ -2130,7 +2148,7 @@ Countly.replaceAllAppKeysInQueueWithCurrentAppKey();
app key. 2. You can remove all requests with a different app key in the request
queue:
-//Removes all requests with a different app key in request queue.
+//Removes all requests with a different app key in request queue.
Countly.removeDifferentAppKeysFromQueue();
In the request queue, if there are any requests whose app key is different than
@@ -2142,7 +2160,7 @@ Countly.removeDifferentAppKeysFromQueue();
when the server is slow or unresponsive. This helps reduce server load and avoid
unnecessary retries. It’s enabled by default but can be disabled if needed.
-CountlyConfig config = CountlyConfig(SERVER_URL, APP_KEY);
+CountlyConfig config = CountlyConfig(SERVER_URL, APP_KEY);
config.disableBackoffMechanism();
For a detailed explanation of how the backoff mechanism works and when it triggers,
@@ -2162,7 +2180,7 @@ config.disableBackoffMechanism();
you don't want to get data older than a certain timeframe, you can configure
the SDK to drop old requests:
-CountlyConfig config = CountlyConfig(SERVER_URL, APP_KEY);
+CountlyConfig config = CountlyConfig(SERVER_URL, APP_KEY);
config.setRequestDropAgeHours(10); // a positive integer indicating hours
By using the setRequestDropAgeHours method while configuring the
@@ -2177,31 +2195,31 @@ config.setRequestDropAgeHours(10); // a positive integer indicating hours
event count reaches a threshold. By default it is 10. If you would like to change
this, call:
-CountlyConfig config = CountlyConfig(SERVER_URL, APP_KEY);
+CountlyConfig config = CountlyConfig(SERVER_URL, APP_KEY);
config.setEventQueueSizeToSend(6);
Checking if the SDK has been initialized
In case you would like to check if init has been called, you may use the following
function:
-Countly.isInitialized();
+Countly.isInitialized();
Server Configuration
Server Configuration is enabled by default. Changes made on SDK Manager SDK Configuration
on your server will affect SDK behavior directly.
-p>
- In all cases, the configuration may not be applied during the app’s first run.
+
+ p In all cases, the configuration may not be applied during the app’s first run.
If this is a security sensitive case for the situations, you can provide the
server config to the SDK during initialization.
-config.setSDKBehaviorSettings("json server config")
+config.setSDKBehaviorSettings("json server config")
If you want to disable automatic config updates from the server, you can prevent
the SDK from making server configuration fetch requests. This is useful if you're
trying to reduce network traffic or control request counts.
-config.disableSDKBehaviorSettingsUpdates()
+config.disableSDKBehaviorSettingsUpdates()
Content Zone
The Content Zone feature enhances user engagement by delivering various types
@@ -2212,7 +2230,7 @@ p>
To start fetching content from the server, use the following method:
-Countly.instance.content.enterContentZone()
+Countly.instance.content.enterContentZone()
This call will retrieve and display any available content for the user. It will
also regularly check if a new content is available, and if it is, will fetch
@@ -2222,16 +2240,16 @@ p>
This regular check happens in every 30 seconds by default. It could be configurable
while initializing the SDK through and it must be greater than 15 seconds.
-countlyConfig.content.setZoneTimerInterval(60) //in seconds
+countlyConfig.content.setZoneTimerInterval(60) //in seconds
If you need to ask for content after a trigger you know you can use this method:
-Countly.instance.content.refreshContentZone()
+Countly.instance.content.refreshContentZone()
When you want to exit from content zone and stop SDK from checking for available
content you can use this method:
-Countly.instance.content.exitContentZone()
+Countly.instance.content.exitContentZone()
To get informed when a user closes a content you can register a global content
callback during SDK initialization:
@@ -2242,8 +2260,8 @@ p>
This feature is not supported in the Web platform.
-countlyConfig.content.setGlobalContentCallback((contentStatus, contentData))
-typedef ContentCallback = void Function(ContentStatus contentStatus, Map<String, dynamic> contentData);
+countlyConfig.content.setGlobalContentCallback((contentStatus, contentData))
+typedef ContentCallback = void Function(ContentStatus contentStatus, Map<String, dynamic> contentData);
The `contentStatus` will indicate either `ContentStatus.completed` or `ContentStatus.closed`.
@@ -2259,43 +2277,39 @@ p>
for enabling advanced features like view name recording and visibility tracking.
These features are currently in a testing phase and might change in future versions.
-CountlyConfig config = CountlyConfig(COUNTLY_APP_KEY, COUNTLY_SERVER_URL);
+CountlyConfig config = CountlyConfig(COUNTLY_APP_KEY, COUNTLY_SERVER_URL);
config.experimental.enablePreviousNameRecording().enableVisibilityTracking();
This class allows enabling two experimental features:
- - Previous Name Recording
- - Visibility Tracking
+ - Previous Name Recording
+ - Visibility Tracking
When you enable previous name recording, it will add previous view name to the
view segmentations (cly_pvn) and previous event name to the event segmentations
(cly_pen).
-config.experimental.enablePreviousNameRecording()
+config.experimental.enablePreviousNameRecording()
When you enable visibility tracking, it will add a parameter (cly_v) to each
recorded event's segmentation about the visibility of the app at the time of
its recording.
-config.experimental.enableVisibilityTracking()
+config.experimental.enableVisibilityTracking()
A/B Experiment Testing
-
- Variant Level Control
-
+Variant Level Control
Platform Info
This feature is not supported in the Web platform.
-
- Downloading
-
+Downloading
You can fetch a map of all A/B testing parameters (keys) and variants associated
with it:
-Countly.instance.remoteConfig.testingDownloadVariantInformation((rResult, error){
+Countly.instance.remoteConfig.testingDownloadVariantInformation((rResult, error){
// do sth
})
@@ -2304,91 +2318,81 @@ config.experimental.enablePreviousNameRecording().enableVisibilityTracking();
-
- Accessing
-
+Accessing
When test variants are fetched, they are saved to the memory. If the memory is
erased, you must fetch the variants again. So a common flow is to use the fetched
values right after fetching them. To access all fetched values, you can use:
-Countly.sharedInstance().remoteConfig().testingGetAllVariants()
+Countly.sharedInstance().remoteConfig().testingGetAllVariants()
This would return a Future<Map<String, List<String>>> where
a test's parameter is associated with all variants under that parameter. The
parameter would be the key, and its value would be a String List of variants.
For example:
-{
+{
"key_1" : ["variant_1", "variant_2"],
"key_2" : ["variant_3"]
}
Or instead you can get the variants of a specific key:
-Countly.sharedInstance().remoteConfig().testingGetVariantsForKey(String valueKey)
+Countly.sharedInstance().remoteConfig().testingGetVariantsForKey(String valueKey)
This would only return a Future<List<String>> of variants for that
specific key. If no variants were present for a key, it would return an empty
list. A typical result would look like this:
-["variant_1", "variant_2"]
+["variant_1", "variant_2"]
-
- Enrolling / Exiting
-
+Enrolling / Exiting
After fetching A/B testing parameters and variants from your server, you next
would like to enroll the user to a specific variant. To do this, you can use
the following method:
-Countly.instance.remoteConfig.testingEnrollIntoVariant(String keyName, String variantName, void Function(RequestResult, String?)? callback)
+Countly.instance.remoteConfig.testingEnrollIntoVariant(String keyName, String variantName, void Function(RequestResult, String?)? callback)
Here the 'valueKey' would be the parameter of your A/B test, and 'variantName'
is the variant you have fetched and selected to enroll for. The callback function
is optional and works the same way as explained above in the Fetching Test Variants
section.
-
- Experiment Level Control
-
+Experiment Level Control
Platform Info
This feature is not supported in the Web platform.
-
- Downloading
-
+Downloading
You can fetch information about the A/B tests in your server including test name,
description and the current variant:
-Countly.instance.remoteConfig.testingDownloadExperimentInformation((rResult, error){
+Countly.instance.remoteConfig.testingDownloadExperimentInformation((rResult, error){
// do sth
-})
+})
You can provide a callback (which is optional) to be called when the fetching
process ends. Depending on the situation, this would return a RequestResponse
Enum (Success, NetworkIssue, or Error) as the first parameter and a String error
as the second parameter if there was an error ("null" otherwise).
-
- Accessing
-
+Accessing
After fetching the experiment information the SDK saves it in the RAM, so if
the memory is erased, you must fetch the information again. You can access this
information through this call:
-Countly.sharedInstance().remoteConfig().testingGetAllExperimentInfo()
+Countly.sharedInstance().remoteConfig().testingGetAllExperimentInfo()
This would return a Future<Map<String, ExperimentInformation>> where
the keys are experiment IDs as String and the values are the ExperimentInformation
Class which contains information about the experiment with that ID. This Class'
structure is like this:
-class ExperimentInformation {
+class ExperimentInformation {
// same ID as used in the map
String experimentID;
// the name of the experiment
@@ -2404,7 +2408,7 @@ config.experimental.enablePreviousNameRecording().enableVisibilityTracking();
-{
+{
some_exp_ID: {
experimentID: some_ID,
experimentName: some_name,
@@ -2423,19 +2427,17 @@ config.experimental.enablePreviousNameRecording().enableVisibilityTracking();
-
- Enrolling / Exiting
-
+Enrolling / Exiting
To enroll a user into the A/B experiment using experiment ID, you use the following
method:
-Countly.instance.remoteConfig.testingEnrollIntoABExperiment(String expID);
+Countly.instance.remoteConfig.testingEnrollIntoABExperiment(String expID);
If you want to remove users from A/B experiment using experiment ID, you can
use the following function:
-Countly.instance.remoteConfig.testingExitABExperiment(String expID);
+Countly.instance.remoteConfig.testingExitABExperiment(String expID);
Extended Device ID Management
@@ -2445,7 +2447,7 @@ config.experimental.enablePreviousNameRecording().enableVisibilityTracking();
You may configure/change the device ID anytime using:
-Countly.changeDeviceId(DEVICE_ID, ON_SERVER);
+Countly.changeDeviceId(DEVICE_ID, ON_SERVER);
You may either allow the device to be counted as a new device or merge existing
data on the server. If theonServer bool is set to
@@ -2462,13 +2464,13 @@ config.experimental.enablePreviousNameRecording().enableVisibilityTracking();
You can enable temporary device ID when initializing the SDK:
-CountlyConfig config = CountlyConfig(SERVER_URL, APP_KEY);
+CountlyConfig config = CountlyConfig(SERVER_URL, APP_KEY);
config.setDeviceId(Countly.deviceIDType["TemporaryDeviceID"]);
// Initialize with that configuration
Countly.initWithConfig(config);
To enable a temporary device ID after init, you would call:
-Countly.changeDeviceId(Countly.deviceIDType["TemporaryDeviceID"], ON_SERVER);
+Countly.changeDeviceId(Countly.deviceIDType["TemporaryDeviceID"], ON_SERVER);
Note: When passing TemporaryDeviceID for
deviceID parameter, argument for onServerparameter
diff --git a/flutter/next.md b/flutter/next.md
index 1d5d7bda..623f86b8 100644
--- a/flutter/next.md
+++ b/flutter/next.md
@@ -15,61 +15,80 @@
4.0, tvOS 10.0, macOS 10.14), and it requires Xcode 13.0+.
- For Android builds, this SDK requires a minimum Android version of 4.2.x (API
- Level 17).
+ For Android builds, this SDK requires a minimum Android version of 5.0 (API Level
+ 21).
For Web builds, this SDK is compatible with browsers that support ECMAScript
5. Minimum versions of major internet browsers that fully support ES5 are:
-
-
-
-
- IE
-
-
- Edge
-
-
- Firefox
-
-
- Firefox (Android)
-
-
- Opera
-
-
- Opera (Mobile)
-
-
- Safari
-
-
- Safari (iOS)
-
-
- Chrome
-
-
- Chrome (Android)
-
-
-
- 10
- 12
- 21
- 96
- 15
- 64
- 6
- 6
- 23
- 98
-
-
-
+
+
+
+
+
+ IE
+
+
+ Edge
+
+
+ Firefox
+
+
+ Firefox (Android)
+
+
+ Opera
+
+
+
+ Opera
+
+
+ (Mobile)
+
+
+
+ Safari
+
+
+
+ Safari
+
+
+ (iOS)
+
+
+
+ Chrome
+
+
+
+ Chrome
+
+
+ (Android)
+
+
+
+
+
+
+ 10
+ 12
+ 21
+ 96
+ 15
+ 64
+ 6
+ 6
+ 23
+ 98
+
+
+
+
To examine the example integrations, please have a look
here.
@@ -78,19 +97,19 @@
Add this to your project's pubspec.yaml file:
-dependencies:
+dependencies:
countly_flutter: ^25.4.0
After you can install packages from the command line with Flutter:
-flutter pub get
+flutter pub get
SDK Integration
Minimal Setup
The shortest way to initialize the SDK, if you want Countly SDK to take care
of device ID seamlessly, is to use the code below.
-// Create the configuration with your app key and server URL
+// Create the configuration with your app key and server URL
CountlyConfig config = CountlyConfig(SERVER_URL, APP_KEY);
// Initialize with that configuration
@@ -123,16 +142,16 @@ Countly.initWithConfig(config).then((value){
SDK Data Storage
SDK data storage locations are platform-specific:
- -
+
-
For iOS, the SDK data is stored in the Application Support
Directory in a file named "Countly.dat"
- -
+
-
For Android, the SDK data is stored in SharedPreferences.
A SharedPreferences object points to a file containing key-value pairs and
provides simple reading and writing methods.
- -
+
-
For Web, the SDK data is stored in Local Storage. It can
be modified to use memory only or cookies.
@@ -145,7 +164,7 @@ Countly.initWithConfig(config).then((value){
We advise doing this while implementing Countly features in your application.
-
CountlyConfig config = CountlyConfig(SERVER_URL, APP_KEY);
+CountlyConfig config = CountlyConfig(SERVER_URL, APP_KEY);
config.setLoggingEnabled(true);
For more information on where to find the SDK logs you can check the documentation
@@ -167,7 +186,7 @@ config.setLoggingEnabled(true);
If you want to enable automatic unhandled crash reporting, you need to call this
before init:
-CountlyConfig config = CountlyConfig(SERVER_URL, APP_KEY);
+CountlyConfig config = CountlyConfig(SERVER_URL, APP_KEY);
config.enableCrashReporting()
By doing that it will automatically catch all errors that are thrown from within
@@ -184,7 +203,7 @@ config.enableCrashReporting()
The following call will add the provided segmentation to all recorded crashes.
Use the following function for this purpose:
-CountlyConfig config = CountlyConfig(SERVER_URL, APP_KEY);
+CountlyConfig config = CountlyConfig(SERVER_URL, APP_KEY);
config.setCustomCrashSegment(Map<String, Object> segments);
Handled Exceptions
@@ -195,17 +214,17 @@ config.setCustomCrashSegment(Map<String, Object> segments);
be provided to the function. A potential use case would be to
exception.toString()
-Countly.logException(String exception, bool nonfatal, [Map<String, Object> segmentation])
+Countly.logException(String exception, bool nonfatal, [Map<String, Object> segmentation])
The issue is recorded with a provided Exception object. If no stacktrace is set,StackTrace.current
will be used.
-Countly.logExceptionEx(Exception exception, bool nonfatal, {StackTrace stacktrace, Map<String, Object> segmentation})
+Countly.logExceptionEx(Exception exception, bool nonfatal, {StackTrace stacktrace, Map<String, Object> segmentation})
The exception/error is recorded through a string message. If no stack trace is
provided, StackTrace.current will be used.
-Countly.logExceptionManual(String message, bool nonfatal, {StackTrace stacktrace, Map<String, Object> segmentation})
+Countly.logExceptionManual(String message, bool nonfatal, {StackTrace stacktrace, Map<String, Object> segmentation})
Below are some examples that how to log handled/nonfatal and unhandled/fatal
exceptions manually.
@@ -213,7 +232,7 @@ config.setCustomCrashSegment(Map<String, Object> segments);
1. Manually report exception
-bool nonfatal = true; // Set it false in case of fatal exception
+bool nonfatal = true; // Set it false in case of fatal exception
// With Exception object
Countly.logExceptionEx(EXCEPTION_OBJECT, nonfatal);
@@ -223,7 +242,7 @@ Countly.logExceptionManual("MESSAGE_STRING", nonfatal);
2. Manually report exception with stack trace
-bool nonfatal = true; // Set it false in case of fatal exception
+bool nonfatal = true; // Set it false in case of fatal exception
// With Exception object
Countly.logExceptionEx(EXCEPTION_OBJECT, nonfatal, stacktrace: STACK_TRACE_OBJECT);
@@ -233,7 +252,7 @@ Countly.logExceptionManual("MESSAGE_STRING", nonfatal, stacktrace: STACK_TRACE_O
3. Manually report exception with segmentation
-bool nonfatal = true; // Set it false in case of fatal exception
+bool nonfatal = true; // Set it false in case of fatal exception
// With Exception object
Countly.logExceptionEx(EXCEPTION_OBJECT, nonfatal, segmentation: {"_facebook_version": "0.0.1"});
@@ -243,7 +262,7 @@ Countly.logExceptionManual("MESSAGE_STRING", nonfatal, segmentation: {"_facebook
4. Manually report exception with stack trace and segmentation
-bool nonfatal = true; // Set it false in case of fatal exception
+bool nonfatal = true; // Set it false in case of fatal exception
// With Exception object
Countly.logExceptionEx(EXCEPTION_OBJECT, nonfatal, STACK_TRACE_OBJECT, {"_facebook_version": "0.0.1"});
@@ -257,7 +276,7 @@ Countly.logExceptionManual("MESSAGE_STRING", nonfatal, STACK_TRACE_OBJECT, {"_fa
will be sent together with the crash report.
The following function call adds a crash breadcrumb:
-Countly.addCrashLog(String logs)
+Countly.addCrashLog(String logs)
Events
Event is any type of action that
@@ -272,7 +291,7 @@ Countly.logExceptionManual("MESSAGE_STRING", nonfatal, STACK_TRACE_OBJECT, {"_fa
In the SDK all event-related functionality can be browsed from the returned interface
on:
-Countly.instance.events
+Countly.instance.events
When providing segmentation for events, the following primitive data types are
supported: "String," "int," "double," and "bool." Additionally, Lists composed
@@ -285,43 +304,43 @@ Countly.logExceptionManual("MESSAGE_STRING", nonfatal, STACK_TRACE_OBJECT, {"_fa
of what information each usage will provide us:
- -
+
-
Usage 1: how many times a purchase event occurred.
- -
+
-
Usage 2: how many times a purchase event occurred + the
total amount of those purchases.
- -
+
-
Usage 3: how many times a purchase event occurred + which
countries and application versions those purchases were made from.
- -
+
-
Usage 4: how many times a purchase event occurred + the
total amount both of which are also available segmented into countries and
application versions.
- -
+
-
Usage 5: how many times purchase event occurred + the total
amount both of which are also available segmented into countries and application
versions + the total duration of those events (under Timed Events topic below).
The function signature as follows
-Future<String?> recordEvent(String key, [Map<String, Object>? segmentation, int? count, double? sum, int? duration])
+Future<String?> recordEvent(String key, [Map<String, Object>? segmentation, int? count, double? sum, int? duration])
1. Event key and count
-Countly.instance.events.recordEvent('purchase', null, 1);
+Countly.instance.events.recordEvent('purchase', null, 1);
2. Event key, count and sum
-Countly.instance.events.recordEvent('purchase', null, 1, 0.99);
+Countly.instance.events.recordEvent('purchase', null, 1, 0.99);
3. Event key and count with segmentation(s)
-Map<String, Object>? segmentation = {
+Map<String, Object>? segmentation = {
'country': 'Germany',
'app_version': '1.0',
'rating': 10,
@@ -337,7 +356,7 @@ Countly.instance.events.recordEvent('purchase', segmentation, 1);
4. Event key, count and sum with segmentation(s)
-Map<String, Object>? segmentation = {
+Map<String, Object>? segmentation = {
'country': 'Germany',
'app_version': '1.0',
'rating': 10,
@@ -353,7 +372,7 @@ Countly.instance.events.recordEvent('purchase', segmentation, 1, 0.99);
5. Event key, count, sum and duration with segmentation(s)
-Map<String, Object>? segmentation = {
+Map<String, Object>? segmentation = {
'country': 'Germany',
'app_version': '1.0',
'rating': 10,
@@ -370,7 +389,7 @@ Countly.instance.events.recordEvent('purchase', segmentation, 1, 0.99, 1);
It's possible to create timed events by defining a start and a stop moment.
-// Basic event
+// Basic event
Countly.instance.events.startEvent("Timed Event");
Timer timer = Timer(new Duration(seconds: 5), () {
@@ -378,9 +397,11 @@ Timer timer = Timer(new Duration(seconds: 5), () {
});
- You may also provide additional information when ending an event. However, in that case, you have to provide the segmentation, count, and sum. The default values for those are "null", 1 and 0.
+ You may also provide additional information when ending an event. However, in
+ that case, you have to provide the segmentation, count, and sum. The default
+ values for those are "null", 1 and 0.
-// Event with Segment, sum and count
+// Event with Segment, sum and count
Countly.instance.events.startEvent("Timed Event With Segment, Sum and Count");
Timer timer = Timer(new Duration(seconds: 5), () {
@@ -400,7 +421,7 @@ Timer timer = Timer(new Duration(seconds: 5), () {
You may cancel the started timed event in case it is not relevant anymore:
-//start some event
+//start some event
Countly.instace.events.startEvent(eventName);
//wait some time
@@ -418,16 +439,16 @@ Countly.instance.events.cancelEvent(eventName);
track a session automatically. This is how it works:
- -
+
-
Start/Begin session Request: It is sent to the server when
the app comes back to the foreground from the background, and it includes
basic metrics.
- -
+
-
Update Session Request: It automatically sends a periodical
(60 sec by default) update session request while the app is in the foreground.
- -
+
-
End Session Request: It is sent at the end of a session
when the app goes to the background or terminates.
@@ -444,22 +465,23 @@ Countly.instance.events.cancelEvent(eventName);
relying on the SDK.
It can be enabled during init with:
-
CountlyConfig config = CountlyConfig(SERVER_URL, APP_KEY);
-config.enableManualSessionHandling();
+CountlyConfig config = CountlyConfig(SERVER_URL, APP_KEY);
+
Afterwards it is up to the implementer to make calls to:
- - Begin session (Starts a session)
- -
+
- Begin session (Starts a session)
+ -
Update session duration (By default, you would call this every 60 seconds
after beginning a session so that it is not closed server side. If you would
- want to increase that duration, you would have to increase the "Maximal Session Duration" in your server API configuration)
+ want to increase that duration, you would have to increase the "Maximal Session
+ Duration" in your server API configuration)
- - End session (Ends and updates duration)
+ - End session (Ends and updates duration)
You can use the 'sessions interface' to make these calls:
-Countly.instance.sessions.beginSession();
+Countly.instance.sessions.beginSession();
Countly.instance.sessions.updateSession();
-Countly.instance.sessions.endSession();
+Countly.instance.sessions.endSession();
View Tracking
@@ -471,7 +493,7 @@ Countly.instance.sessions.endSession();
The SDK provides access to all view-related functionality through the interface
returned by:
-Countly.instance.views
+Countly.instance.views
Manual View Recording
You can manually track views in your application.
@@ -488,12 +510,12 @@ Countly.instance.sessions.endSession();
If you want to start a view that will be automatically stopped when starting
another view, use the following method:
-// record a view on your application
-final String? viewID = await Countly.instance.views.startAutoStoppedView("Dashboard");
+// record a view on your application
+final String? viewID = await Countly.instance.views.startAutoStoppedView("Dashboard");
You can also specify the custom segmentation key-value pairs while starting views:
-Map<String, Object> segmentation = {
+Map<String, Object> segmentation = {
"country": "Germany",
"app_version": "1.0",
"rating": 10,
@@ -504,21 +526,21 @@ final String? viewID = await Countly.instance.views.startAutoStoppedView
"sub_names": ["John", "Doe", "Jane"]
};
-final String? anotherViewID = Countly.instance.views.startAutoStoppedView("HomePage", segmentation);
+final String? anotherViewID = Countly.instance.views.startAutoStoppedView("HomePage", segmentation);
Regular Views
Opposed to "auto stopped views", with regular views you can have multiple of
them started at the same time, and then you can control them independently. You
- can manually start a view using the startViewmethod with a view name. This will start tracking a view and return a unique identifier, and the view will remain active until explicitly stopped using stopViewWithName or stopViewWithID
+ can manually start a view using the startViewmethod with a view name. This will start tracking a view and return a unique identifier, and the view will remain active until explicitly stopped using stopViewWithName or stopViewWithID
-// record a view on your application
+// record a view on your application
Countly.instance.views.startView("HomePage");
final String? viewID = await Countly.instance.views.startView("Dashboard");
You can also specify the custom segmentation key-value pairs while starting views:
-Map<String, Object> segmentation = {
+Map<String, Object> segmentation = {
"country": "Germany",
"app_version": "1.0",
"rating": 10,
@@ -537,30 +559,30 @@ final String? anotherViewID = Countly.instance.views.startView("HomePage", segme
you try to stop one with that name, the SDK would close one of those randomly.
Below you can see example ways of stopping views.
-Countly.instance.views.stopViewWithName("HomePage");
+Countly.instance.views.stopViewWithName("HomePage");
This function allows you to manually stop the tracking of a view identified by
its name. You can also specify the custom segmentation key-value pairs while stopping views:
-Countly.instance.views.stopViewWithName("HomePage", segmentation);
+Countly.instance.views.stopViewWithName("HomePage", segmentation);
You can also stop view tracking by its unique idetifier using
- stopViewWithID
+ stopViewWithID
-Countly.instance.views.stopViewWithID(viewID);
+Countly.instance.views.stopViewWithID(viewID);
You can also specify the custom segmentation key-value pairs while stopping views:
-Countly.instance.views.stopViewWithID(anotherViewID, segmentation);
+Countly.instance.views.stopViewWithID(anotherViewID, segmentation);
You can stop all views tracking using
- stopAllViews
+ stopAllViews
-Countly.instance.views.stopAllViews();
+Countly.instance.views.stopAllViews();
You can also specify the custom segmentation key-value pairs while stopping all views:
-Countly.instance.views.stopAllViews(segmentation);
+Countly.instance.views.stopAllViews(segmentation);
Pausing and Resuming Views
This SDK allows you to start multiple
@@ -571,18 +593,19 @@ final String? anotherViewID = Countly.instance.views.startView("HomePage", segme
You can pause view tracking by its unique identifier using
- pauseViewWithID
+ pauseViewWithID
-Countly.instance.views.pauseViewWithID(viewID);
+Countly.instance.views.pauseViewWithID(viewID);
- This function temporarily pauses the tracking of a view identified by its unique identifier.
+ This function temporarily pauses the tracking of a view identified by its unique
+ identifier.
- You can resume view tracking by its unique identifier using resumeViewWithID:
+ You can resume view tracking by its unique identifier using resumeViewWithID:
-Countly.instance.views.resumeViewWithID(viewID);
+Countly.instance.views.resumeViewWithID(viewID);
- This function resumes the tracking of a view identified by its unique identifier.
+ This function resumes the tracking of a view identified by its unique identifier.
Adding Segmentation to Started Views
@@ -599,7 +622,7 @@ final String? anotherViewID = Countly.instance.views.startView("HomePage", segme
Here is an example on how to achieve that using the view name:
-String viewName = 'HomePage';
+String viewName = 'HomePage';
await Countly.instance.views.startView(viewName);
Map<String, Object> segmentation = {
@@ -617,7 +640,7 @@ await Countly.instance.views.addSegmentationToViewWithName(viewName, segmentatio
Here is an example for how to add segmentation to a view using its ID:
-String? viewID = await Countly.instance.views.startView('HomePage');
+String? viewID = await Countly.instance.views.startView('HomePage');
Map<String, Object> segmentation = {
"country": "Germany",
@@ -642,7 +665,7 @@ await Countly.instance.views.addSegmentationToViewWithID(viewID!, segmentation);
For setting global segmentation values during SDK initialization, use the following
method:
-// set global segmentation at initialization
+// set global segmentation at initialization
final CountlyConfig config = CountlyConfig(SERVER_URL, APP_KEY);
config.setGlobalViewSegmentation(segmentation);
@@ -653,12 +676,12 @@ config.setGlobalViewSegmentation(segmentation);
ThesetGlobalViewSegmentation method will replace the previously
set values..
-Countly.instance.views.setGlobalViewSegmentation(segmentation);
+Countly.instance.views.setGlobalViewSegmentation(segmentation);
The updateGlobalViewSegmentation method will modify the previously
set values and overwrite any previously set keys.
-Countly.instance.views.updateGlobalViewSegmentation(segmentation);
+Countly.instance.views.updateGlobalViewSegmentation(segmentation);
Device ID Management
A device ID is a unique identifier for your users. You may specify the device
@@ -669,7 +692,7 @@ config.setGlobalViewSegmentation(segmentation);
You may provide your custom device ID when initializing the SDK:
-CountlyConfig config = CountlyConfig(SERVER_URL, APP_KEY);
+CountlyConfig config = CountlyConfig(SERVER_URL, APP_KEY);
config.setDeviceId(DEVICE_ID);
Changing the Device ID
@@ -680,7 +703,7 @@ config.setDeviceId(DEVICE_ID);
You may configure or change the device ID anytime using:
-Countly.instance.deviceId.setID(DEVICE_ID);
+Countly.instance.deviceId.setID(DEVICE_ID);
When using setID, the SDK determines internally if the device will
be counted as a new device on the server or if it will merge the new and old
@@ -706,7 +729,7 @@ config.setDeviceId(DEVICE_ID);
You can enable temporary device ID mode when initializing the SDK:
-CountlyConfig config = CountlyConfig(SERVER_URL, APP_KEY);
+CountlyConfig config = CountlyConfig(SERVER_URL, APP_KEY);
config.enableTemporaryDeviceIDMode();
// Initialize with that configuration
@@ -714,7 +737,7 @@ Countly.initWithConfig(config);
To enable a temporary device ID after initialization, you can call:
-Countly.instance.deviceId.enableTemporaryIDMode();
+Countly.instance.deviceId.enableTemporaryIDMode();
The SDK will be in temporary device ID mode, all requests will be on hold and
they will be persistently stored.
@@ -733,23 +756,20 @@ Countly.initWithConfig(config);
You may want to see what the current device ID is. For that, you can use the
following call:
-String? currentDeviceId = Countly.instance.deviceId.getID();
+String? currentDeviceId = Countly.instance.deviceId.getID();
- You can use getIDType method which returns a
- DeviceIDType to get the current device ID type. The ID type is an enum with the possible values of:
+ You can use getIDType method which returns a
+ DeviceIDType to get the current device ID type. The ID type is an
+ enum with the possible values of:
- -
- "DEVELOPER_SUPPLIED" - device ID was supplied by the host app.
-
- -
- "SDK_GENERATED" - device ID was generated by the SDK.
-
- -
- "TEMPORARY_ID" - the SDK is in temporary device ID mode.
+
-
+ "DEVELOPER_SUPPLIED" - device ID was supplied by the host app.
+ - "SDK_GENERATED" - device ID was generated by the SDK.
+ - "TEMPORARY_ID" - the SDK is in temporary device ID mode.
-DeviceIdType? deviceIdType = await Countly.instance.deviceId.getIDType();
+DeviceIdType? deviceIdType = await Countly.instance.deviceId.getIDType();
Device ID Generation
When the SDK is initialized for the first time with no device ID, it will generate
@@ -792,7 +812,7 @@ Countly.initWithConfig(config);
android/app/src/main/AndroidManifest.xml inside
application tag.
-<application ...>
+<application ...>
...
<service android:name="ly.count.dart.countly_flutter.CountlyMessagingService">
<intent-filter>
@@ -804,7 +824,7 @@ Countly.initWithConfig(config);
Step 6: Add the following line in file android/build.gradle
-buildscript {
+buildscript {
dependencies {
classpath 'com.google.gms:google-services:LATEST'
}
@@ -819,7 +839,7 @@ Countly.initWithConfig(config);
Step 7: Add the following line in file android/app/build.gradle
-dependencies {
+dependencies {
implementation 'ly.count.android:sdk:LATEST'
implementation 'com.google.firebase:firebase-messaging:LATEST'
}
@@ -851,15 +871,15 @@ apply plugin: 'com.google.gms.google-services'
For this purpose you can find CountlyNotificationService.h/m file
under:
-Pods/Development Pods/Countly/{PROJECT_NAME}/ios/.symlinks/plugins/countly_flutter/ios/Classes/CountlyiOS/CountlyNotificationService.h/m
+Pods/Development Pods/Countly/{PROJECT_NAME}/ios/.symlinks/plugins/countly_flutter/ios/Classes/CountlyiOS/CountlyNotificationService.h/m
Some tips to find the files from deep hierarchy:
- -
+
-
You can filter the files in the navigator using a shortcut ⌥⌘J (Option-Command-J),
in the filter box type "CountlyNotificationService" and it will show the
related files only.
- -
+
-
You can find the file using the shortcut ⇧⌘O (Shift-Command-O) and then navigate
to that file using the shortcut ⇧⌘J (Shift-Command-J)
@@ -874,18 +894,18 @@ apply plugin: 'com.google.gms.google-services'
token mode. This would allow you to choose either test or production modes, push
token mode should be set before init.
-
// Set messaging mode for push notifications
+// Set messaging mode for push notifications
Countly.pushTokenType(Countly.messagingMode["TEST"]);
When you are finally ready to initialise Countly push, you would call this:
-// This method will ask for permission, enables push notification and send push token to countly server.
+// This method will ask for permission, enables push notification and send push token to countly server.
Countly.askForNotificationPermission();
Also it is important to note that push notification is enabled for iOS by default,
so to disable you need to call disablePushNotifications method:
-// Disable push notifications feature for iOS, by default it is enabled.
+// Disable push notifications feature for iOS, by default it is enabled.
Countly.disablePushNotifications();
Removing Push and Its Dependencies
@@ -898,18 +918,18 @@ Countly.disablePushNotifications();
To register a Push Notification callback after initializing the SDK, use the
method below.
-Countly.onNotification((String notification) {
+Countly.onNotification((String notification) {
print(notification);
-});
+});
In order to listen to notification receive and click events, Place below code
in AppDelegate.swift
Add header files
-import countly_flutter
+import countly_flutter
Add these methods:
-// Required for the notification event. You must call the completion handler after handling the remote notification.
+// Required for the notification event. You must call the completion handler after handling the remote notification.
func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject], fetchCompletionHandler completionHandler: (UIBackgroundFetchResult) -> Void) {
CountlyFlutterPlugin.onNotification(userInfo);
completionHandler(.newData);
@@ -938,7 +958,7 @@ override func userNotificationCenter(\_ center: UNUserNotificationCenter, didRec
Here is the example of how data will receive in push callbacks:
Data Received for Android platform:
-{
+{
"c.e.cc": "TR",
"c.e.dt": "mobile",
"Key": "value",
@@ -950,9 +970,9 @@ override func userNotificationCenter(\_ center: UNUserNotificationCenter, didRec
"sound": "custom",
"title": "title",
"message": "Message"
-}
+}
Data Received for iOS platform:
-{
+{
Key = value;
aps = {
alert = {
@@ -973,7 +993,7 @@ override func userNotificationCenter(\_ center: UNUserNotificationCenter, didRec
i = 62b5b945cabedb0870e9f217;
l = "https://www.google.com/";
};
-}
+}
User Location
@@ -996,23 +1016,21 @@ override func userNotificationCenter(\_ center: UNUserNotificationCenter, didRec
When setting user location information, you would be setting these values:
- -
+
-
countryCode a string in ISO 3166-1 alpha-2 format country code
- -
+
-
city a string specifying city name
- -
+
-
location a string comma-separated latitude and longitude
- -
+
-
IP a string specifying an IP address in IPv4 or IPv6 formats
-
- All values are optional, but at least one should be set.
-
-// Example for setLocation
+All values are optional, but at least one should be set.
+// Example for setLocation
CountlyConfig config = CountlyConfig(SERVER_URL, APP_KEY);
config.setLocation(country_code: 'TR', city: 'Istanbul', gpsCoordinates: '41.0082,28.9784', ipAddress: '10.2.33.12')
@@ -1020,7 +1038,7 @@ config.setLocation(country_code: 'TR', city: 'Istanbul', gpsCoordinates: '41.008
SDK has started. To do so, use the setUserLocation method as shown
below.
-// Example for setUserLocation
+// Example for setUserLocation
Countly.setUserLocation(countryCode: 'TR', city: 'Istanbul', gpsCoordinates: '41.0082,28.9784', ipAddress: '10.2.33.12');
Disable Location
@@ -1030,7 +1048,7 @@ Countly.setUserLocation(countryCode: 'TR', city: 'Istanbul', gpsCoordinates: '41
setUserLocation is called with any non-null value, tracking will
resume.
-//disable location tracking
+//disable location tracking
Countly.disableLocation();
Remote Config
@@ -1050,9 +1068,7 @@ Countly.disableLocation();
stay as they were. A previously valid key may return no value after a full download.
-
- Downloading Values
-
+ Downloading Values
Automatic Remote Config Triggers
@@ -1067,17 +1083,17 @@ Countly.disableLocation();
The automatic download triggers that would trigger a full value download are:
- -
+
-
when the SDK has finished initializing
- -
+
-
after the device ID is changed without merging
- -
+
-
when user gets out of temp ID mode
- -
- when
CountlyConsent.remoteConfig consent is given after it had been removed before (if consents are enabled)
+ -
+ when
CountlyConsent.remoteConfig consent is given after it had been removed before (if consents are enabled)
@@ -1085,7 +1101,7 @@ Countly.disableLocation();
enableRemoteConfigAutomaticTriggers on the configuration
object you will provide during init.
- CountlyConfig config = CountlyConfig(SERVER_URL, APP_KEY)
+ CountlyConfig config = CountlyConfig(SERVER_URL, APP_KEY)
..enableRemoteConfigAutomaticTriggers(); // necessary to enable the feature
@@ -1094,33 +1110,33 @@ Countly.disableLocation();
were not updated, you would have metadata indicating if a value belongs to
the old or current user.
- CountlyConfig config = CountlyConfig(SERVER_URL, APP_KEY)
- ..enableRemoteConfigValueCaching();
+ CountlyConfig config = CountlyConfig(SERVER_URL, APP_KEY)
+ ..enableRemoteConfigValueCaching();
Manually Calls
There are three ways to trigger remote config value download manually:
- -
+
-
Manually downloading all keys
- -
+
-
Manually downloading specific keys
- - Manually downloading, omitting (everything except) keys.
+ - Manually downloading, omitting (everything except) keys.
Each of these calls also has an optional parameter that you can provide a RCDownloadCallback to, which would be triggered when the download attempt has finished.
- dowloadAllKeys is the same as the automatically triggered update - it replaces all stored values with the ones from the server (all locally stored values are deleted and replaced with new ones).
+ dowloadAllKeys is the same as the automatically triggered update - it replaces all stored values with the ones from the server (all locally stored values are deleted and replaced with new ones).
- Or you might only want to update specific key values. To do so, you will need to call downloadSpecificKeys to downloads new values for the wanted keys. Those are provided with a String array.
+ Or you might only want to update specific key values. To do so, you will need to call downloadSpecificKeys to downloads new values for the wanted keys. Those are provided with a String array.
- Or you might want to update all the values except a few defined keys. To do so, call downloadOmittingKeys would update all values except the provided keys. The keys are provided with a String array.
+ Or you might want to update all the values except a few defined keys. To do so, call downloadOmittingKeys would update all values except the provided keys. The keys are provided with a String array.
- Countly.instance.remoteConfig.downloadAllKeys((rResult, error, fullValueUpdate, downloadedValues) {
+ Countly.instance.remoteConfig.downloadAllKeys((rResult, error, fullValueUpdate, downloadedValues) {
if (rResult == RequestResult.Success) {
// do sth
} else {
@@ -1138,7 +1154,7 @@ Countly.disableLocation();
});
- Countly.instance.remoteConfig.downloadSpecificKeys(List<String> keysToInclude, (rResult, error, fullValueUpdate, downloadedValues) {
+ Countly.instance.remoteConfig.downloadSpecificKeys(List<String> keysToInclude, (rResult, error, fullValueUpdate, downloadedValues) {
if (rResult == RequestResult.Success) {
// do sth
} else {
@@ -1147,7 +1163,7 @@ Countly.disableLocation();
});
- Countly.instance.remoteConfig.downloadOmittingKeys(List<String> keysToExclude, (rResult, error, fullValueUpdate, downloadedValues) {
+ Countly.instance.remoteConfig.downloadOmittingKeys(List<String> keysToExclude, (rResult, error, fullValueUpdate, downloadedValues) {
if (rResult == RequestResult.Success) {
// do sth
} else {
@@ -1157,7 +1173,7 @@ Countly.disableLocation();
- When making requests with an "inclusion" or "exclusion" array, if those arrays are empty or null, they will function the same as a dowloadAllKeys request and will update all the values. This means it will also erase all keys not returned by the server.
+ When making requests with an "inclusion" or "exclusion" array, if those arrays are empty or null, they will function the same as a dowloadAllKeys request and will update all the values. This means it will also erase all keys not returned by the server.
Accessing Values
@@ -1165,10 +1181,10 @@ Countly.disableLocation();
key. This returns an Future<RCData> object that contains the value of the
key and the metadata about that value's owner. If value in RCData was
null
- then no value was found or the value was null.
+ then no value was found or the value was null.
-Object? value_1 = await Countly.instance.remoteConfig.getValue("key_1").value;
+Object? value_1 = await Countly.instance.remoteConfig.getValue("key_1").value;
Object? value_2 = await Countly.instance.remoteConfig.getValue("key_2").value;
Object? value_3 = await Countly.instance.remoteConfig.getValue("key_3").value;
Object? value_4 = await Countly.instance.remoteConfig.getValue("key_4").value;
@@ -1182,10 +1198,10 @@ JSONObject jObj = value4 as JSONObject;
If you want to get all values together you can use
getAllValues which returns a Future<Map<String,
RCData>>.
- The SDK does not know the returned value type, so, it will return the Object. The developer then needs to cast it to the appropriate type. The returned values may also be JSONArray, JSONObject,
+ The SDK does not know the returned value type, so, it will return the Object. The developer then needs to cast it to the appropriate type. The returned values may also be JSONArray, JSONObject,
or just a simple value, such as int.
-Map<String, RCData> allValues = await Countly.instance.remoteConfig.getAllValues();
+Map<String, RCData> allValues = await Countly.instance.remoteConfig.getAllValues();
int intValue = allValues["key_1"] as int;
double doubleValue = allValues["key_2"] as double;
@@ -1197,7 +1213,7 @@ JSONObject jObj = allValues["key_4"] as JSONObject;
belongs to. The isCurrentUsersData is only false when there was a device ID change,
but somehow (or intentionally) a remote config value was not updated.
-Class RCData {
+Class RCData {
Object value;
Boolean isCurrentUsersData;
}
@@ -1211,14 +1227,14 @@ JSONObject jObj = allValues["key_4"] as JSONObject;
At some point, you might like to erase all the values downloaded from the server. You will need to call one function to do so.
-Countly.instance.remoteConfig.clearAll();
+Countly.instance.remoteConfig.clearAll();
Global Download Callbacks
Also, you may provide a global callback function to be informed when the remote
config download request is finished with
remoteConfigRegisterGlobalCallback during the SDK initialization:
-CountlyConfig config = CountlyConfig(SERVER_URL, APP_KEY)
+CountlyConfig config = CountlyConfig(SERVER_URL, APP_KEY)
..remoteConfigRegisterGlobalCallback((rResult, error, fullValueUpdate, downloadedValues) {
if (error != null) {
// do sth
@@ -1230,24 +1246,24 @@ JSONObject jObj = allValues["key_4"] as JSONObject;
and it would have the following parameters:
- -
+
-
rResult: RequestResult Enum (either
- Error, Success or NetworkIssue)
+ Error, Success or NetworkIssue)
- -
+
-
error: String (error message. "null" if there is
no error)
- -
+
-
fullValueUpdate: boolean ("true" - all values updated,
"false" - a subset of values updated)
- -
+
-
downloadedValues: Map<String, RCData> (the
whole downloaded remote config values)
-RCDownloadCallback {
+RCDownloadCallback {
void callback(RequestResult rResult, String error, boolean fullValueUpdate, Map<String, RCData> downloadedValues)
}
@@ -1261,7 +1277,7 @@ JSONObject jObj = allValues["key_4"] as JSONObject;
You can also register (or remove) callbacks to do different things after the
SDK initialization. You can register these callbacks multiple times:
-// register a callback
+// register a callback
Countly.instance.remoteConfig.registerDownloadCallback((rResult, error, fullValueUpdate, downloadedValues) {
// do sth
});
@@ -1276,36 +1292,34 @@ Countly.instance.remoteConfig.removeDownloadCallback((rResult, error, fullValueU
from some or all existing A/B tests available.
-
- Enrollment on Download
-
+ Enrollment on Download
You can enroll into the A/B tests automatically whenever you download RC
values from the server. To do so you have to set the following flag at the
config object during initialization:
- CountlyConfig config = CountlyConfig(SERVER_URL, APP_KEY)
-..enrollABOnRCDownload();
-
- Enrollment on Access
-
+ CountlyConfig config = CountlyConfig(SERVER_URL, APP_KEY)
+..enrollABOnRCDownload();
+ Enrollment on Access
- You can also enroll to A/B tests while getting RC values from storage. You can use getValueAndEnroll while getting a single value and getAllValuesAndEnroll while getting all values to enroll to the keys that exist. If no value was stored for those keys these functions would not enroll the user. Both of these functions works the same way with their non-enrolling variants, namely; getValue and getAllValues.
+ You can also enroll to A/B tests while getting RC values from storage. You
+ can use getValueAndEnroll while getting a single value and
+ getAllValuesAndEnroll while getting all values to enroll to
+ the keys that exist. If no value was stored for those keys these functions
+ would not enroll the user. Both of these functions works the same way with
+ their non-enrolling variants, namely; getValue and
+ getAllValues.
-
- Enrollment on Action
-
+ Enrollment on Action
To enroll a user into the A/B tests for the given keys you use the following
method:
- Countly.instance.remoteConfig.enrollIntoABTestsForKeys(List<String> keys);
+ Countly.instance.remoteConfig.enrollIntoABTestsForKeys(List<String> keys);
Here the keys array is the mandatory parameter for this method to work.
-
- Exiting A/B Tests
-
+ Exiting A/B Tests
Platform Info
@@ -1317,7 +1331,7 @@ Countly.instance.remoteConfig.removeDownloadCallback((rResult, error, fullValueU
If you want to remove users from A/B tests of certain keys you can use the following
function:
-Countly.instance.remoteConfig.exitABTestsForKeys(List<String> keys);
+Countly.instance.remoteConfig.exitABTestsForKeys(List<String> keys);
Here if no keys are provided it would remove the user from all A/B tests instead.
@@ -1349,12 +1363,12 @@ Countly.instance.remoteConfig.removeDownloadCallback((rResult, error, fullValueU
dashboard. If the user dismisses star rating dialog without giving a rating,
the event will not be recorded.
-Countly.askForStarRating();
+Countly.askForStarRating();
The star-rating dialog's title, message, and dismiss button text may be customized
through the following functions:
-CountlyConfig config = CountlyConfig(SERVER_URL, APP_KEY);
+CountlyConfig config = CountlyConfig(SERVER_URL, APP_KEY);
config.setStarRatingTextTitle("Custom title"); // Only available for Android
config.setStarRatingTextMessage("Custom message");
config.setStarRatingTextDismiss("Custom message"); // Only available for Android
@@ -1368,10 +1382,10 @@ config.setStarRatingTextDismiss("Custom message"); // Only available for Android
It is possible to display 3 kinds of feedback widgets:
- NPS,
- Survey,
+ NPS,
+ Survey,
and
- Rating.
+ Rating.
For more detailed information about Feedback Widgets, you can refer to
@@ -1387,12 +1401,12 @@ config.setStarRatingTextDismiss("Custom message"); // Only available for Android
After you have created widgets on your dashboard, you can reach the methods to
show them from the feedback interface of your Countly instance:
-Countly.instance.feedback
+Countly.instance.feedback
You can display a random active widget for the widget type you want with one
of these methods:
-.presentNPS([String? nameIDorTag, FeedbackCallback? feedbackCallback])
+.presentNPS([String? nameIDorTag, FeedbackCallback? feedbackCallback])
.presentRating([String? nameIDorTag, FeedbackCallback? feedbackCallback])
.presentSurvey([String? nameIDorTag, FeedbackCallback? feedbackCallback])
@@ -1420,7 +1434,7 @@ Countly.instance.feedback.presentNPS("MyNetPromoterScore", FeedbackCallback(
For more in-depth information on retrieving feedback widgets, understanding object
structures, or presenting them yourself, please refer to the following
- resource.
+ resource.
Manual Reporting
@@ -1445,7 +1459,7 @@ Countly.instance.feedback.presentNPS("MyNetPromoterScore", FeedbackCallback(
In case you want to use with callback then you can call 'getFeedbackWidgetData'
in this way:
-Countly.getFeedbackWidgetData(chosenWidget, onFinished: (retrievedWidgetData, error) {
+Countly.getFeedbackWidgetData(chosenWidget, onFinished: (retrievedWidgetData, error) {
if (error == null) {
}
});
@@ -1453,7 +1467,7 @@ Countly.instance.feedback.presentNPS("MyNetPromoterScore", FeedbackCallback(
If you want to use it without a callback then you can call 'getFeedbackWidgetData'
in this way:
-List result = await Countly.getFeedbackWidgetData(chosenWidget);
+List result = await Countly.getFeedbackWidgetData(chosenWidget);
String? error = result[1];
if (error == null) {
Map<String, dynamic> retrievedWidgetData = result[0];
@@ -1468,7 +1482,7 @@ if (error == null) {
use it, the widgetInformation and the widgetData to report the feedback result
with the following call:
-