From f51111719543fecc8cb1b0914f8ee1eeccbd17a6 Mon Sep 17 00:00:00 2001 From: Arif Burak Demiray Date: Thu, 16 Apr 2026 16:01:30 +0300 Subject: [PATCH] feat: cpp 26.1.0 --- cpp/23.2.md | 828 +++++++++++++++++++++++++++++++++++++++++++++++++ cpp/current.md | 694 ++++++++++++++++++++++++++++++----------- cpp/next.md | 527 +++++++++++++++++++++---------- 3 files changed, 1718 insertions(+), 331 deletions(-) create mode 100644 cpp/23.2.md diff --git a/cpp/23.2.md b/cpp/23.2.md new file mode 100644 index 00000000..a39ffafa --- /dev/null +++ b/cpp/23.2.md @@ -0,0 +1,828 @@ +

+ This documentation is for the Countly CPP SDK version 23.2.X. The SDK source + code repository can be found + here. +

+
+

+ Click + here, to + access the documentation for older SDK versions. +

+
+

Supported Platforms are Windows, GNU/Linux, and Mac OS X.

+

+ To examine the example integrations please have a look + here. +

+

Adding the SDK to the Project

+

+ Countly C++ SDK has been designed to work with very few dependencies in order + 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
+
+

+ If submodules in your project are empty you can run this command at root of your + project: +

+
+
git submodule update --init --recursive
+
+

+ If you want to use SQLite to store session data persistently, build sqlite: +

+
+
# 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
+cd build
+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
+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. +

+

+ Build with the option COUNTLY_BUILD_TESTS and + COUNTLY_BUILD_SAMPLEON to build executables + to run the tests and the sample app. Also set the + COUNTLY_USE_SQLITEON to use SQLite in your + project. +

+
+
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
+
+
+

SDK Integration

+

Minimal Setup

+

+ Before you can use any functionality, you have to initiate the SDK. +

+

+ 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);
+

+ Here, you have to provide your appKey, and your Countly server URL. Please check + here + for more information on how to acquire your application key (APP_KEY) and server + URL. +

+

+ The third parameter with typeint is a virtual port number and it + is an optional parameter with a default value of 0 (expected + range is 0 to 65535).
+ The last bool parameter is also optional with the default value + of false. When you set this value to true SDK automatically + extends the session every 60 seconds. +

+
+

+ If you are in doubt about the correctness of your Countly SDK integration, + you can learn more about methods to verify it from + here. +

+
+

SDK Data Storage

+

+ In its unconfigured state, the SDK stores everything in memory. +

+

+ There is an alternative SDK configuration option where the SDK will try to save + data peristently with SQLite. In that case you would provide a path and filename + where the database would be located. More information on that can be found + here. +

+

SDK Notes

+

+ To access the Countly Global Instance use the following code snippet: +

+
cly::Countly::getInstance()
+

SDK Logging

+

+ 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);
+
+

Crash Reporting

+

+ The Countly SDK for C++ can collect + Crash Reports, + which you may examine and resolve later on the server. +

+

+ In the SDK all crash-related functionalities can be browsed from the returned + interface on: +

+
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*/
+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: +

+ +

+ crashMetrics is a map that contains the core information about the + crash you want to capture and report. If it is not properly formed, your Countly + server would not be able to recognize and interpret your crash report and you + would not be able to observe the error from your server. There are two mandatory + key-value pairs that you need to fill when forming the + crashMetrics object. These are _os and_app_version keys. Any other + 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;
+
+/*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*/
+

Crash Breadcrumbs

+

+ Throughout your app, you can leave crash breadcrumbs. They are short string logs + that would ideally describe the previous steps that were taken in your app before + 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");
+

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.  +

+

+ There are a couple of values that can be set when recording an event. The main + one is the key property which would be the identifier/name for + that event. For example, in case a user buys an item in your game, you can create + an event with the key 'purchase' to inform this action on your Countly server. +

+

+ Optionally there are also other properties that you might want to set: +

+ +

Recording Events

+

+ 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);
+

+ 2. Event key, count, and sum +

+
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);
+

+ 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);
+

+ 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);
+

+ 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);
+
+//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);
+
+

Sessions

+

Automatic Sessions

+

+ By default, 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: +

+
cly::Countly::getInstance().setAutomaticSessionUpdateInterval(10);
+

+ The SDK ends the current session whenever the user exits from the app. +

+

Manual Sessions

+

+ If you need a custom session logic then you can use manual session methods. First + you need to enable manual session control: +

+
cly::Countly::getInstance().enableManualSessionControl();
+

It needs to be called before SDK is initialized.

+

Afterwards it is up to implementer to set up session logic:

+ +

You can use below functions to set up your session logic

+
cly::Countly::getInstance().beginSession();
+cly::Countly::getInstance().updateSession();
+cly::Countly::getInstance().endSession();
+

View Tracking

+

Manual View Recording

+

+ The Countly C++ SDK supports manual view (screen) tracking, with which, you can + 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");
+

+ 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);
+
+

+ When the screen closes you can report it to the server by using one of the following + methods: +

+

+ 1. Ending a view with a view ID: +

+

+ 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.  +

+

For example:

+
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: +

+
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 + data to its fullest potential, click + here. +

+

Device ID management

+

+ A device ID is a unique identifier of your users. You have to specify the device + ID yourself. When providing one you should keep in mind that it has to be unique + for all users. Some potential sources for such an ID may be the user's username, + email, or some other internal ID used within your other systems. +

+

+ In the C++ SDK the device ID is not persistent and has to be provided every time + you start the SDK: +

+
+
cly::Countly::getInstance().setDeviceID("UNIQUE_DEVICE_ID");
+
+

Changing Device ID

+

+ In case your application authenticates users, you might want to change the initial + device ID of the user to another one in your backend after the user logs in. + This helps you identify a specific user with a specific ID on a device the user + logs in, and the same scenario can also be used in cases where same user logs + in using a different device (e.g another tablet, another mobile phone, or web). + If you employ this logic, any data stored in your Countly server database associated + 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);
+

+ 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 + (an account change), or your app enters a state where you no longer can verify + 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);
+

+ Doing it this , will prevent the previous user's data to merge with the new id. +

+

+ If the second parameter same_user is set to true, the + old device ID on the server will be replaced with the new one, and data associated + with the old device ID will be merged automatically. +

+

+ Otherwise, if same_user is set to false, the device + will be counted as a new device on the server. +

+

User Location

+

+ While integrating this SDK into your application, you might want to track your + user's location. You could use this information to learn more about your app’s + user base. There are 4 fields that can be provided: +

+ +

Setting Location

+

+ During init, you may set location, and after the SDK initialization, this location + 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);
+
+

+ Note that the IP address would only be updated if it's set during the init process. +

+

+ When these values are set after the SDK initialization, a separate request will + be created to send them to the server. Except for the IP address, because Countly + server can process an IP address only when starting a new session. +

+

+ If you don't want to set specific fields, you should set them to empty. +

+

Remote Config

+

+ Available in the Enterprise Edition, Remote Config allows you to modify how your + app functions or looks by requesting key-value pairs from your Countly server. + The returned values may be modified based on the user profile. For more details, + please see the + Remote Config documentation. +

+

Manual Remote Config

+

+ To download Remote Config, call 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");
+

+ It returns a value of the type json. +

+

User profiles

+

+ For information about User Profiles, review + this documentation. +

+

+ If a property is set as an empty string, it will be deleted from the user on + the server side. +

+

Setting Predefined Values

+

+ The Countly C++ SDK allows you to upload user specific data to your Countly server. + You may set the data against predefined keys for a particular user. +

+

The keys for predefined user data fields are as follows:

+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
KeyTypeDescription
namestringUser's full name
usernamestringUser's nickname
emailstringUser's email address
organizationstringUser's organization name
phonestringUser's phone number
picturestringURL to avatar or profile picture of the user
genderstringUser's gender as M for male and F for female
byearstringUser'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);
+
+

Setting Custom Values

+

+ 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);
+
+

Setting User Picture

+

+ 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);
+
+

Security and Privacy

+

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 + &checksum field. You will need to set the exact same + salt on the Countly server. If the salt on the Countly + server is set, all requests would be checked for the validity of the + &checksum the field before being processed. +

+
cly::Countly.getInstance().setSalt("salt");
+

Other Features and Notes

+

SDK Config Parameters Explained

+

+ These are the methods that lets you configure the Countly SDK. They need to be + called before SDK is initialized. +

+ +

Check Queue Sizes

+

SDK exposes some functions to observe queue sizes:

+
checkEQSize() // returns event queue size
+checkRQSize() // returns request queue size
+

Example Integrations

+

+ example_integration.cpp + covers basic functionalities. +

+

Setting Event Queue Threshold

+

+ Before or after SDK starts, you can set a threshold for the number of events + that the system can record internally (on memory or persistently) before they + are all sent to the request queue.
+ Example: +

+
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. + The default value is set to 100, but you can change it to any whole number between + 1 to 10000. +

+

Setting Request Queue Processing Batch Size

+

+ Before or after SDK starts, you can set a threshold for the number of requests + the request queue can try sending to the server in one go. If this threshold + is high and there are an increased number of requests in the queue, it can prevent + the application from closing until it sends all requests to the server. To prevent + this from happening, the SDK stops this process whenever the threshold is reached + (100 by default) and starts again at the next iteration of the update loop.
+ Example: +

+
cly::Counlty.getInstance().setMaxRQProcessingBatchSize(10);
+

Setting Up SQLite Storage

+

+ In case you need persistent storage, you would need to build the SDK with that + option enabled. In that case, you must build the SDK with the COUNTLY_USE_SQLITE + option, as explained here. +

+

+ You would also need to provide a path before initialization where the database + file could be stored. +

+
+
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. +

+

Custom Metrics

+

+ 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);
+

Setting Custom SHA-256

+

+ C++ SDK allows users to set a custom SHA-256 method for calculating the checksum + of request data. +

+

+ To use the custom SHA-256 feature follow the following steps: +

+

+ 1. Build the Countly C++ SDK executable with the + COUNTLY_USE_CUSTOM_SHA256 option. +

+
+
cmake -DCOUNTLY_USE_SQLITE=1 -DCOUNTLY_USE_CUSTOM_SHA256=1 -B build
+
+

+ 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);
+
+

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
+cmake -DCMAKE_INSTALL_PREFIX:PATH=/usr/local -DBUILD_SHARED_LIBS=OFF -B build
+
+cd build
+#build the SDK
+make
+
+#install countly on the system
+make install
+ +

FAQ

+

What Information is Collected by the SDK

+

+ 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. + Here + is the collected data for the SDK. +

\ No newline at end of file diff --git a/cpp/current.md b/cpp/current.md index fe624e47..a1282e3d 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 26.1.X. The SDK source + code repository can be found + here.

Click - here, to + here, to access the documentation for older SDK versions.

+

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.

Adding the SDK to the Project

@@ -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,13 @@ 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. + information here.

Build with the option COUNTLY_BUILD_TESTS and @@ -67,9 +65,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

SDK Integration

@@ -80,10 +79,12 @@ make ./countly-tests # run unit test
make ./countly-sample # run sample ap

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 +100,9 @@ make ./countly-tests # run unit test
make ./countly-sample # run sample ap

If you are in doubt about the correctness of your Countly SDK integration, you can learn more about methods to verify it from - here. + here.

-

SDK Logging

-

- 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);
-

Device ID

-

- 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");
-

SDK Data Storage

In its unconfigured state, the SDK stores everything in memory. @@ -140,7 +117,44 @@ 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()
+

SDK Logging

+

+ 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 Countly object + to enable logging: +

+
void printLog(cly::LogLevel level, const string& msg) {...}
+...
+
+void (*logger_function)(cly::LogLevel level, const std::string& message);
+logger_function = printLog;
+cly::Countly::getInstance().setLogger(logger_function);
+
+

+ The LogLevel enum has the following values: +

+
    +
  • + cly::LogLevel::DEBUG - Detailed information for debugging. +
  • +
  • + cly::LogLevel::INFO - General information about SDK operations. +
  • +
  • + cly::LogLevel::WARNING - Warnings about potential issues. +
  • +
  • + cly::LogLevel::ERROR - Error conditions. +
  • +
  • + cly::LogLevel::FATAL - Critical errors that prevent SDK operation. +
  • +

Crash Reporting

The Countly SDK for C++ can collect @@ -151,33 +165,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()

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*/
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::string> 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.
@@ -191,7 +221,30 @@ make ./countly-tests # run unit test
make ./countly-sample # run sample ap 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;

/*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::string> 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*/

Crash Breadcrumbs

Throughout your app, you can leave crash breadcrumbs. They are short string logs @@ -199,10 +252,14 @@ 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");
+

+ The SDK stores a maximum of 100 breadcrumbs by default. When + this limit is reached, the oldest breadcrumb is removed before adding a new one. +

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 @@ -214,95 +271,156 @@ make ./countly-tests # run unit test
make ./countly-sample # run sample ap 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;
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. 

Timed Events

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);
+
+

Flushing Events

+

+ If you would like to force send all queued events to the server immediately, + you can use the flushEvents method. This will block until all events + are sent or the timeout is reached. +

+
// Flush with default 30 second timeout
+cly::Countly::getInstance().flushEvents();
+
+// Flush with custom timeout
+cly::Countly::getInstance().flushEvents(std::chrono::seconds(10));

Sessions

-

Automatic Session Tracking

+

Automatic Sessions

+

+ By default, 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: +

+
cly::Countly::getInstance().setAutomaticSessionUpdateInterval(10);

- 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:
+ The SDK ends the current session whenever the user exits from the app.

-
cly::Countly::getInstance().setAutomaticSessionUpdateInterval(10);
+

Manual Sessions

- The SDK ends the current session whenever the user exits from the app. + If you need a custom session logic then you can use manual session methods. First + you need to enable manual session control:

+
cly::Countly::getInstance().enableManualSessionControl();
+

It needs to be called before SDK is initialized.

+

Afterwards it is up to implementer to set up session logic:

+
    +
  • 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) +
  • +
  • End session (Ends and updates duration)
  • +
+

You can use below functions to set up your session logic

+
cly::Countly::getInstance().beginSession();
+cly::Countly::getInstance().updateSession();
+cly::Countly::getInstance().endSession();

View Tracking

Manual View Recording

@@ -310,12 +428,19 @@ 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 = {
{"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 +450,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 +476,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");
+

Changing Device ID

In case your application authenticates users, you might want to change the initial @@ -362,7 +492,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 +500,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 +520,12 @@ cly::Countly.getInstance().addEvent(event); user base. There are 4 fields that can be provided:

Setting Location

@@ -403,7 +533,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.

@@ -423,18 +560,31 @@ cly::Countly.getInstance().addEvent(event); please see the Remote Config documentation.

+

Enabling Remote Config

+

+ Before using Remote Config, you need to enable it. Call + enableRemoteConfig() before starting the SDK: +

+
cly::Countly::getInstance().enableRemoteConfig();
+cly::Countly::getInstance().start("YOUR_APP_KEY", "https://try.count.ly", 443, true);

Manual Remote Config

To download Remote Config, call updateRemoteConfig().

-
cly::Countly.getInstance().updateRemoteConfig();
+
cly::Countly::getInstance().updateRemoteConfig();
+

If you only want to update specific keys, you can use:

+
std::string keys[] = {"key1", "key2"};
+cly::Countly::getInstance().updateRemoteConfigFor(keys, 2);
+

Or if you want to update all keys except specific ones:

+
std::string excludeKeys[] = {"key1", "key2"};
+cly::Countly::getInstance().updateRemoteConfigExcept(excludeKeys, 2);

Accessing Remote Config Values

To access the stored config, call - cly::Countly.getInstance().getRemoteConfigValue(const std::string& key). + 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.

@@ -443,6 +593,10 @@ cly::Countly.getInstance().addEvent(event); For information about User Profiles, review this documentation.

+

+ If a property is set as an empty string, it will be deleted from the user on + the server side. +

Setting Predefined Values

The Countly C++ SDK allows you to upload user specific data to your Countly server. @@ -450,77 +604,104 @@ cly::Countly.getInstance().addEvent(event);

The keys for predefined user data fields are as follows:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
KeyTypeDescription
namestringUser's full name
usernamestringUser's nickname
emailstringUser's email address
organizationstringUser's organization name
phonestringUser's phone number
picturestringURL to avatar or profile picture of the user
genderstringUser's gender as M for male and F for female
byearstringUser's year of birth as integer
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
KeyTypeDescription
namestringUser's full name
usernamestringUser's nickname
emailstringUser's email address
organizationstringUser's organization name
phonestringUser's phone number
picturestringURL to avatar or profile picture of the user
genderstringUser's gender as M for male and F for female
byearstringUser'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);
+

Setting Custom Values

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);
+

Setting User Picture

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::Countly::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 @@ -529,8 +710,148 @@ 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");
+

+ You can also manually calculate the checksum for request data using: +

+
std::string checksum = cly::Countly::getInstance().calculateChecksum("salt", "request_data");

Other Features and Notes

+

SDK Config Parameters Explained

+

+ These are the methods that lets you configure the Countly SDK. They need to be + called before SDK is initialized. +

+ +

Custom HTTP Client

+

+ It is possible to provide a custom HTTP client for the SDK. This is useful when + you need to handle networking in a specific way for your platform or when you + are using the COUNTLY_USE_CUSTOM_HTTP build option. +

+

+ Your custom HTTP client function must match the + HTTPClientFunction signature and return an + HTTPResponse struct: +

+
// HTTPResponse structure
+struct HTTPResponse {
+  bool success;
+  nlohmann::json data;
+};
+
+// Custom HTTP client function
+cly::HTTPResponse myHTTPClient(bool is_post, const std::string& url, const std::string& data) {
+  cly::HTTPResponse response;
+  response.success = false;
+
+  // Implement your HTTP logic here
+  // 'is_post' indicates whether to use POST or GET
+  // 'url' is the full URL to send the request to
+  // 'data' is the request payload
+
+  // On success, set response.success = true
+  // and parse the server response into response.data
+
+  return response;
+}
+
+// Set the custom HTTP client before starting the SDK
+cly::Countly::getInstance().setHTTPClient(myHTTPClient);
+

+ The custom HTTP client needs to be set before the SDK is initialized. +

+

SDK Behavior Settings

+

+ The SDK periodically fetches behavior settings from the server to adjust its + internal configuration. If you want to disable these periodic updates, you can + call the following method before SDK initialization: +

+
cly::Countly::getInstance().disableSDKBehaviorSettingsUpdates();
+

+ Alternatively, you can provide SDK behavior settings manually in JSON format + before SDK initialization: +

+
std::string settings = "{\"your_settings_json\": \"value\"}";
+cly::Countly::getInstance().setSDKBehaviorSettings(settings);
+

+ Both methods need to be called before the SDK is initialized. +

+

Immediate Request On Stop

+

+ By default, the SDK's internal update loop uses a polling-based sleep mechanism. + This means that when you call stop() or + setUpdateInterval(), the change does not take effect until the + current sleep interval expires (default is 3 seconds). +

+

+ If you enable enableImmediateRequestOnStop(), the update loop + switches to using a condition variable instead of polling. This allows + stop() and setUpdateInterval() to wake the update + loop immediately, so changes take effect without waiting for the current + interval to finish. +

+

+ This method must be called before the SDK is initialized: +

+
cly::Countly& countly = cly::Countly::getInstance();
+countly.enableImmediateRequestOnStop();
+countly.start("YOUR_APP_KEY", "https://try.count.ly", 443, true);
+

+ When this feature is enabled: +

+ +

+ This feature is disabled by default. When disabled, the SDK uses the standard + sleep-based approach where stop() and + setUpdateInterval() wait for the current interval to expire + before taking effect. +

+

Check Queue Sizes

+

SDK exposes some functions to observe queue sizes:

+
checkEQSize() // returns event queue size
+checkRQSize() // returns request queue size
+

Example Integrations

+

+ example_integration.cpp + covers basic functionalities. +

Setting Event Queue Threshold

Before or after SDK starts, you can set a threshold for the number of events @@ -538,7 +859,7 @@ cly::Countly.getInstance().addEvent(event); are all sent to the request queue.
Example:

-
cly::Counlty.getInstance().setEventsToRQThreshold(10);
+
cly::Countly::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 +876,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::Countly::getInstance().setMaxRQProcessingBatchSize(10);

Setting Up SQLite Storage

In case you need persistent storage, you would need to build the SDK with that @@ -567,13 +888,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.

+

Custom Metrics

+

+ 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);

Setting Custom SHA-256

C++ SDK allows users to set a custom SHA-256 method for calculating the checksum @@ -593,28 +929,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);
-

Additional project install option

+
std::string customChecksumCalculator(const std::string& data) {
+  ...
+  return result;
+} 
+
+
+cly::Countly& countly = cly::Countly::getInstance();
+
+countly.setSalt("salt");
+countly.setSha256(customChecksumCalculator);
+
+

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

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.

\ No newline at end of file diff --git a/cpp/next.md b/cpp/next.md index 03ed51f1..a1282e3d 100644 --- a/cpp/next.md +++ b/cpp/next.md @@ -1,12 +1,12 @@

- This documentation is for the Countly CPP SDK version 23.2.X. The SDK source + This documentation is for the Countly CPP SDK version 26.1.X. The SDK source code repository can be found here.

Click - here, to + here, to access the documentation for older SDK versions.

@@ -21,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
@@ -49,14 +49,13 @@ 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. + information here.

Build with the option COUNTLY_BUILD_TESTS and @@ -66,7 +65,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 +79,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 +117,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()

SDK Logging

The first thing you should do while integrating our SDK is to enable logging. @@ -126,16 +125,36 @@ countly.start("YOUR_APP_KEY", "https://try.count.ly", 443, true); state and about encountered problems.

- Set setLogger(logger_function) on the Counlty object + Set setLogger(logger_function) on the Countly object to enable logging:

-
void printLog(cly::Countly::LogLevel level, const string& msg) {...}
+
void printLog(cly::LogLevel level, const string& msg) {...}
 ...
 
-void (*logger_function)(cly::Countly::LogLevel level, const std::string& message);
+void (*logger_function)(cly::LogLevel level, const std::string& message);
 logger_function = printLog;
 cly::Countly::getInstance().setLogger(logger_function);
 
+

+ The LogLevel enum has the following values: +

+
    +
  • + cly::LogLevel::DEBUG - Detailed information for debugging. +
  • +
  • + cly::LogLevel::INFO - General information about SDK operations. +
  • +
  • + cly::LogLevel::WARNING - Warnings about potential issues. +
  • +
  • + cly::LogLevel::ERROR - Error conditions. +
  • +
  • + cly::LogLevel::FATAL - Critical errors that prevent SDK operation. +
  • +

Crash Reporting

The Countly SDK for C++ can collect @@ -146,21 +165,21 @@ 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"},
 };
 
 /*should create the crashMetrics map*/
-std::map<std::string, std::any="std::any"> crashMetrics;
+std::map<std::string, std::string> crashMetrics;
 
 /*mandatory values*/
 crashMetrics["_os"] = "Android"; # your OS info
@@ -174,21 +193,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 +221,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::string> crashMetrics;
 
 /*mandatory values*/
 crashMetrics["_os"] = "Android"; /*your OS info*/
@@ -215,17 +234,17 @@ crashMetrics["_device"] = "Galaxy S4"; /*model for Android, iPhone1,1 etc for iO
 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["_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*/
+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*/

Crash Breadcrumbs

Throughout your app, you can leave crash breadcrumbs. They are short string logs @@ -233,10 +252,14 @@ 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");
+

+ The SDK stores a maximum of 100 breadcrumbs by default. When + this limit is reached, the oldest breadcrumb is removed before adding a new one. +

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 +271,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();
@@ -338,11 +361,11 @@ event.startTimer();
 //end the timer and record event
 event.stopTimer();
 
-cly::Countly.getInstance().addEvent(event);
+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
@@ -350,18 +373,54 @@ event.addSegmentation("country", "Germany");
 
 ...
 
-cly::Countly.getInstance().addEvent(event);
+cly::Countly::getInstance().addEvent(event);
 
+

Flushing Events

+

+ If you would like to force send all queued events to the server immediately, + you can use the flushEvents method. This will block until all events + are sent or the timeout is reached. +

+
// Flush with default 30 second timeout
+cly::Countly::getInstance().flushEvents();
+
+// Flush with custom timeout
+cly::Countly::getInstance().flushEvents(std::chrono::seconds(10));

Sessions

-

Automatic Session Tracking

+

Automatic Sessions

- 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:
+ By default, 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: +

+
cly::Countly::getInstance().setAutomaticSessionUpdateInterval(10);
+

+ The SDK ends the current session whenever the user exits from the app.

-
cly::Countly::getInstance().setAutomaticSessionUpdateInterval(10);
+

Manual Sessions

- The SDK ends the current session whenever the user exits from the app. + If you need a custom session logic then you can use manual session methods. First + you need to enable manual session control:

+
cly::Countly::getInstance().enableManualSessionControl();
+

It needs to be called before SDK is initialized.

+

Afterwards it is up to implementer to set up session logic:

+
    +
  • 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) +
  • +
  • End session (Ends and updates duration)
  • +
+

You can use below functions to set up your session logic

+
cly::Countly::getInstance().beginSession();
+cly::Countly::getInstance().updateSession();
+cly::Countly::getInstance().endSession();

View Tracking

Manual View Recording

@@ -369,12 +428,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 +450,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 +479,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 +492,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 +500,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 +520,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 +533,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"; 
@@ -501,18 +560,31 @@ cly::Countly::getInstance().setLocation(countryCode, city, latitude + "," + long
   please see the
   Remote Config documentation.
 

+

Enabling Remote Config

+

+ Before using Remote Config, you need to enable it. Call + enableRemoteConfig() before starting the SDK: +

+
cly::Countly::getInstance().enableRemoteConfig();
+cly::Countly::getInstance().start("YOUR_APP_KEY", "https://try.count.ly", 443, true);

Manual Remote Config

To download Remote Config, call updateRemoteConfig().

-
cly::Countly.getInstance().updateRemoteConfig();
+
cly::Countly::getInstance().updateRemoteConfig();
+

If you only want to update specific keys, you can use:

+
std::string keys[] = {"key1", "key2"};
+cly::Countly::getInstance().updateRemoteConfigFor(keys, 2);
+

Or if you want to update all keys except specific ones:

+
std::string excludeKeys[] = {"key1", "key2"};
+cly::Countly::getInstance().updateRemoteConfigExcept(excludeKeys, 2);

Accessing Remote Config Values

To access the stored config, call - cly::Countly.getInstance().getRemoteConfigValue(const std::string& key). + 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 +594,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 +604,65 @@ cly::Countly::getInstance().setLocation(countryCode, city, latitude + "," + long

The keys for predefined user data fields are as follows:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
KeyTypeDescription
namestringUser's full name
usernamestringUser's nickname
emailstringUser's email address
organizationstringUser's organization name
phonestringUser's phone number
picturestringURL to avatar or profile picture of the user
genderstringUser's gender as M for male and F for female
byearstringUser's year of birth as integer
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
KeyTypeDescription
namestringUser's full name
usernamestringUser's nickname
emailstringUser's email address
organizationstringUser's organization name
phonestringUser's phone number
picturestringURL to avatar or profile picture of the user
genderstringUser's gender as M for male and F for female
byearstringUser'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"},
@@ -595,7 +672,7 @@ cly::Countly::getInstance().setLocation(countryCode, city, latitude + "," + long
   {"byear", "1991"},
   {"organization", "Organization"},
 };
-cly::Countly.getInstance().setUserDetails(userdetail);
+cly::Countly::getInstance().setUserDetails(userdetail);
 

Setting Custom Values

@@ -603,12 +680,12 @@ 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"}
 };
 
-cly::Countly.getInstance().setCustomUserDetails(userdetail);
+cly::Countly::getInstance().setCustomUserDetails(userdetail);
 

Setting User Picture

@@ -616,15 +693,15 @@ 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"},
 };
 
-cly::Counlty.getInstance().setUserDetails(userdetail);
+cly::Countly::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 +710,143 @@ 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");
+

+ You can also manually calculate the checksum for request data using: +

+
std::string checksum = cly::Countly::getInstance().calculateChecksum("salt", "request_data");

Other Features and Notes

+

SDK Config Parameters Explained

+

+ These are the methods that lets you configure the Countly SDK. They need to be + called before SDK is initialized. +

+
    +
  • + disableAutoEventsOnUserProperties() - Disables sending events + on user property calls. +
  • +
  • + setUpdateInterval(size_t milliseconds) - By default SDK + processes request queue and event queue in every 3 seconds. It can be configurable + via this method. +
  • +
  • + alwaysUsePost(bool value) - Set to send all requests made + to the Countly server using HTTP POST. By default it is HTTP GET. +
  • +
  • + setMaxRequestQueueSize(unsigned int requestQueueSize) - + Sets the new maximum size for the request queue. +
  • +
  • + setHTTPClient(HTTPClientFunction fun) - It is possible to + provide custom HTTP client for the SDK. +
  • +
  • + enableImmediateRequestOnStop() - Enables the use of a + condition variable in the update loop, allowing stop() and + setUpdateInterval() to take effect immediately instead of + waiting for the current sleep interval to expire. Disabled by default. +
  • +
+

Custom HTTP Client

+

+ It is possible to provide a custom HTTP client for the SDK. This is useful when + you need to handle networking in a specific way for your platform or when you + are using the COUNTLY_USE_CUSTOM_HTTP build option. +

+

+ Your custom HTTP client function must match the + HTTPClientFunction signature and return an + HTTPResponse struct: +

+
// HTTPResponse structure
+struct HTTPResponse {
+  bool success;
+  nlohmann::json data;
+};
+
+// Custom HTTP client function
+cly::HTTPResponse myHTTPClient(bool is_post, const std::string& url, const std::string& data) {
+  cly::HTTPResponse response;
+  response.success = false;
+
+  // Implement your HTTP logic here
+  // 'is_post' indicates whether to use POST or GET
+  // 'url' is the full URL to send the request to
+  // 'data' is the request payload
+
+  // On success, set response.success = true
+  // and parse the server response into response.data
+
+  return response;
+}
+
+// Set the custom HTTP client before starting the SDK
+cly::Countly::getInstance().setHTTPClient(myHTTPClient);
+

+ The custom HTTP client needs to be set before the SDK is initialized. +

+

SDK Behavior Settings

+

+ The SDK periodically fetches behavior settings from the server to adjust its + internal configuration. If you want to disable these periodic updates, you can + call the following method before SDK initialization: +

+
cly::Countly::getInstance().disableSDKBehaviorSettingsUpdates();
+

+ Alternatively, you can provide SDK behavior settings manually in JSON format + before SDK initialization: +

+
std::string settings = "{\"your_settings_json\": \"value\"}";
+cly::Countly::getInstance().setSDKBehaviorSettings(settings);
+

+ Both methods need to be called before the SDK is initialized. +

+

Immediate Request On Stop

+

+ By default, the SDK's internal update loop uses a polling-based sleep mechanism. + This means that when you call stop() or + setUpdateInterval(), the change does not take effect until the + current sleep interval expires (default is 3 seconds). +

+

+ If you enable enableImmediateRequestOnStop(), the update loop + switches to using a condition variable instead of polling. This allows + stop() and setUpdateInterval() to wake the update + loop immediately, so changes take effect without waiting for the current + interval to finish. +

+

+ This method must be called before the SDK is initialized: +

+
cly::Countly& countly = cly::Countly::getInstance();
+countly.enableImmediateRequestOnStop();
+countly.start("YOUR_APP_KEY", "https://try.count.ly", 443, true);
+

+ When this feature is enabled: +

+
    +
  • + Calling stop() will immediately wake the update loop and + shut down the SDK without waiting for the remaining sleep duration. +
  • +
  • + Calling setUpdateInterval() will immediately wake the update + loop so the new interval is applied right away. +
  • +
+

+ This feature is disabled by default. When disabled, the SDK uses the standard + sleep-based approach where stop() and + setUpdateInterval() wait for the current interval to expire + before taking effect. +

+

Check Queue Sizes

+

SDK exposes some functions to observe queue sizes:

+
checkEQSize() // returns event queue size
+checkRQSize() // returns request queue size

Example Integrations

example_integration.cpp @@ -647,7 +859,7 @@ cly::Counlty.getInstance().setUserDetails(userdetail); are all sent to the request queue.
Example:

-
cly::Counlty.getInstance().setEventsToRQThreshold(10);
+
cly::Countly::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 +876,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::Countly::getInstance().setMaxRQProcessingBatchSize(10);

Setting Up SQLite Storage

In case you need persistent storage, you would need to build the SDK with that @@ -676,7 +888,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 +906,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,38 +929,41 @@ 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;
 } 
 
 
-cly::Countly& countly = cly::Countly.getInstance();
+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 +976,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