This documentation is for the Countly Unity SDK version 24.8.X. The SDK source code repository can be found here.
Click here, to access the documentation for older SDK versions.
The SDK requires the .NET profile to be at least ".NET 4.x" (".NET Framework " or ".NET Standard 2.1" are acceptable targets)
The SDK is validated against the following platforms: Android, iOS, Windows, UWP, Linux, and Mac OSX. The SDK is also validated against the following LTS versions: 2020.X, 2021.X, 2022.X, and 2023.X.
To examine the example integrations, please have a look here.
SDK Integration
Adding the SDK to the Project
Download the Unity package from GitHub and import it into your project.
To import the package (right click on Assets => Import Package => Custom Package => Path_To_Package) and leave all the files checked because we need to import all the files in the package.
This SDK uses the Newtonsoft Json package internally and it is required for the SDK to work.
Since Unity version 2020 this package is added to your project automatically by Unity. For versions before that, (2018 and 2019) you have to install this package in the project manually.
One way to do Install the Newtonsoft Json package would be to use the built-in package manager. You would go to Windows => Package Manager. In there you would see something like this:
If the Newtonsoft Json package doesn't appear in the Package Manager, you can add it manually. To achieve this, you would need to add:
"com.unity.nuget.newtonsoft-json": "3.0.2"
This line, in the "manifest.json" file. After adding the line and saving it, the package would be added automatically. It should also appear in the Package Manager afterward.
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:
string appKey = "COUNTLY_APP_KEY";
string serverUrl = "COUNTLY_SERVER_URL";
CountlyConfiguration config = new CountlyConfiguration(appKey, serverUrl);
Countly.Instance.Init(config);
In the CountlyConfiguration
object, you provide appKey and your Countly server URL. Please check here for more information on how to acquire your application key (APP_KEY) and server URL.
If you are in doubt about the correctness of your Countly SDK integration you can learn about the verification methods from here.
Required App Permissions
If you expect the game to be saved on an SD card or any other type of external storage, set Write Permission to 'External (SDCard). This can be found in your Android platform settings under 'Other Settings'.
When configuring your app, make sure that it has permission to access the internet.
SDK Data Storage
Countly SDK store data that are meant for your app's use only, within an internal storage volume. If your game saves in external storage, SDK will store data within external storage. You may need to add permission to store data on an SD card. Please read the Required app permissions section for more information.
SDK uses Preferences to keep track of application and user preferences and store private, primitive data in key-value pairs. Operational data is stored in iBoxDB database file, named 'db3.box'.
The specific path where the database file would be stored is different for different platforms. To determine the specific path where the database is stored, you would look at the value returned by Application.persistentDataPath
.
Following are the locations of the database file were used in our sample app:
- Android: '/storage/emulated/0/Android/data/ly.count.demo/files/db3.box'
- Linux: 'home/<username>/.config/unity3d/Countly/CountlyDotNetSDK/db3.box'
- Windows: 'C:/Users/<username>/AppData/LocalLow/Countly/CountlyDotNetSDK/db3.box'
- Mac OSX: '~/Library/Application Support/Countly/CountlyDotNetSDK/db3.box'
- iOS: '/var/mobile/Containers/Data/Application/<random-folder-name>/Documents/db3.box'
SDK Notes
To access the Countly Global Instance use the following code snippet:
Countly.Instance
SDK Logging / Debug Mode
The first thing you should do while integrating our SDK is enabling logging. If logging is enabled, then our SDK will print out debug messages about its internal state and encountered problems.
Call EnableLogging
on the config object to enable logging:
CountlyConfiguration config = new CountlyConfiguration(appKey, serverUrl)
.EnableLogging();
For more information on where to find the SDK logs you can check the documentation here.
Crash Reporting
The Countly SDK for Unity 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.Instance.CrashReports
Automatic Crash Handling
The Unity SDK can automatically report uncaught exceptions/crashes in the application to the Countly server. This feature is enabled by default. In order to, stop reporting uncaught exceptions/crashes automatically, call DisableAutomaticCrashReporting(), in the SDK configuration.
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 exception use the following code snippet:
await Countly.Instance.CrashReports.SendCrashReportAsync(ex.Message, ex.StackTrace, null, false);
Here is the detail of the parameters:
- message - (Mandatory, string) a string that contains a detailed description of the exception.
- stackTrace - (Mandatory, string) a string that describes the contents of the call stack.
- segments - (Optional, IDictionary<string, string>) custom key/values to be reported.
- nonfatal - (Optional, bool) set false if the error is fatal.
Example:
try {
throw new DivideByZeroException();
} catch (Exception ex) {
await Countly.Instance.CrashReports.SendCrashReportAsync(ex.Message, ex.StackTrace);
}
You can also send a segmentation with an exception.
Dictionary<string, object> segmentation = new Dictionary<string, object>();
segmentation.Add("Action", "click");
try {
throw new DivideByZeroException();
} catch (Exception ex) {
await Countly.Instance.CrashReports.SendCrashReportAsync(ex.Message, ex.StackTrace, segmentation, true);
}
If you have handled an exception and it turns out to be fatal to your app, you may use the following calls:
await Countly.Instance.CrashReports.SendCrashReportAsync(ex.Message, ex.StackTrace, null, false);
Dictionary<string, object> segmentation = new Dictionary<string, object>();
segmentation.Add("Action", "click");
await Countly.Instance.CrashReports.SendCrashReportAsync(ex.Message, ex.StackTrace, segmentation, false);
Crash Breadcrumbs
Throughout your app, you can leave crash breadcrumbs. They are short logs that would 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.Instance.CrashReports.AddBreadcrumbs("breadcrumb");
Consent
This feature uses Crashes
consent. No additional crash logs will be recorded if consent is required and not given.
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.
The Unity SDK helps record as many events as you want (you can set a threshold limit during initialization), and the system will send them automatically to the server once the threshold limit is reached. By default, Countly tracks only up to 100 events. However, this is also configurable.
In the SDK, all event-related functionalities can be browsed from the returned interface on:
Countly.Instance.Events
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 purchased an item in a game, you could create an event with the key 'purchase'.
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 that is 1.
- sum - this value would be summed across all events in the dashboard. For example, in-app purchase events sum of purchased items. Its default value is 0.
- duration - used to record and track 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 types for the value are "String", "Integer", "Double", and "Boolean". All other types will be ignored.
Recording Events
Here is a quick way to record an event:
public async Task RecordEventAsync(string key, IDictionary<string, object> segmentation = null, int? count = 1, double? sum = 0, double? duration = null);
Based on the example below of an event recording a purchase, here is a quick summary of the information for each usage:
- Usage 1: how many times the purchase event occurred.
- Usage 2: how many times the purchase event occurred + the total amount of those purchases.
- Usage 3: how many times the purchase event occurred + from which countries and application versions those purchases were made.
- Usage 4: how many times the purchase event occurred + the total amount, both of which are also available, segmented into countries and application versions.
- Usage 5: how many times the purchase event occurred + the total amount, both of which are also available, segmented by countries and application versions + the total duration of those events.
1. Event key and count
await Countly.Instance.Events.RecordEventAsync(key: "purchase", count: 1);
2. Event key, count, and sum
await Countly.Instance.Events.RecordEventAsync(key: "purchase", count: 1, sum: 0.99);
3. Event key and count with segmentation(s)
Dictionary<string, object> segmentation = new Dictionary<string, object>();
segmentation.Add("country", "Germany");
segmentation.Add("app_version", "1.0");
await Countly.Instance.Events.RecordEventAsync(key: "purchase", segmentation: segmentation, count: 1);
4. Event key, count, and sum with segmentation(s)
Dictionary<string, object> segmentation = new Dictionary<string, object>();
segmentation.Add("country", "Germany");
segmentation.Add("app_version", "1.0");
await Countly.Instance.Events.RecordEventAsync(key: "purchase", segmentation: segmentation, count: 1, sum: 0.99);
5. Event key, count, sum, and duration with segmentation(s)
Dictionary<string, object> segmentation = new Dictionary<string, object>();
segmentation.Add("country", "Germany");
segmentation.Add("app_version", "1.0");
await Countly.Instance.Events.RecordEventAsync(key: "purchase", segmentation: segmentation, count: 1, sum: 0.99, duration: 60);
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, and 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.
string eventName = "Some event";
//start some event
Countly.Instance.Events.StartEvent(eventName);
//wait some time
//end the event
Countly.Instance.Events.EndEvent(eventName);
You may also provide additional information when ending an event. In that case you can provide the segmentation, count, or sum values. The default values for those are "null", 1, and 0.
string eventName = "Some event";
//start some event
Countly.Instance.Events.StartEvent(eventName);
//wait some time
IDictionary<string, object> segmentation = new Dictionary<string, object>();
segmentation.Add("wall", "orange");
//end the event while also providing segmentation information
Countly.Instance.Events.EndEvent(eventName, segmentation);
Here are other options to end timed events:
//end the event while providing segmentation information and count
Countly.Instance.Events.EndEvent("timed-event", segmentation, 4);
//end the event while providing segmentation information, count and sum
Countly.Instance.Events.EndEvent("timed-event", segmentation, 4, 10);
You may cancel the started timed event in case it is not relevant anymore:
//start some event
Countly.Instance.Events.StartEvent(eventName);
//wait some time
//cancel the event
Countly.Instance.Events.CancelEvent(eventName);
Consent
This feature uses Events
consent. No additional events will be recorded if consent is required and not given.
When consent is removed, all previously started timed events will be canceled.
Sessions
Automatic Session Tracking
The Unity SDK handles the session automatically. After calling the Init method, the SDK starts the session automatically and extending the session after every 60 seconds. This value is configured during initialization. It cannot be modified after initialization.
The SDK ends the current session whenever the user quits the app or app goes into the background. A session would be started again when the app comes to the foreground.
Disable Automatic Session Tracking
You might want to disable automatic session tracking. To do so, use the following code snippet before init call.
config.DisableAutomaticSessionTracking();
Note that after disabling session tracking, the following things would happen:
- Session information would not be recorded
- Device metrics would not be recorded
- On dashboard location map would not be updated and, overview and analytics session related to sessions and users would all be empty
Consent
This feature requiresSessions
consent. Sessions and metrics will not be recorded if consent is required and not given.
If consent was given and then is removed, the current session will not get explicitly ended.
If consent was removed and then given, and automatic sessions were enabled, a session will automatically be started.
View Tracking
In the SDK, all view-related functionality can be browsed from the returned interface on:
Countly.Instance.Views
To review the resulting data from view tracking, open the dashboard and go to Analytics > Views
. For more information on using view tracking data to its fullest potential, click here.
Manual View Recording
The SDK provides various ways to track views. You can track a single view at a given time or multiple views according to your needs. Each view has its unique view ID, which can be used to manipulate the view further.
Auto Stopped Views
An easy way to track views is by using the auto-stopped views. These views would stop if another view starts. You can start an auto-stopped view with or without segmentation like this:
// without segmentation
Countly.Instance.Views.StartAutoStoppedView("View Name");
// Or with segmentation
Dictionary<string, object> viewSegmentation = new Dictionary<string, object>();
viewSegmentation.Add("Game", "Space Invaders");
viewSegmentation.Add("HighScore", 999999);
viewSegmentation.Add("Accuracy", 99.99);
viewSegmentation.Add("IsMultiplayer", false);
viewSegmentation.Add("ResponseTime", 0.45f);
viewSegmentation.Add("PlayerID", 2468135790L);
Countly.Instance.Views.StartAutoStoppedView("View Name", viewSegmentation);
It would return a string view ID:
string id = Countly.Instance.Views.StartAutoStoppedView("View Name");
Regular Views
Opposed to auto-stopped views, with regular views, you can have multiple of them started simultaneously, and then you can control them independently.
You can start a view that would not close when another view starts like this:
Countly.Instance.Views.StartView("View Name");
While manually tracking views, you may add your custom segmentation to them like this:
Dictionary<string, object> viewSegmentation = new Dictionary<string, object>();
viewSegmentation.Add("Class", "Wizard");
viewSegmentation.Add("Level", 42);
viewSegmentation.Add("ManaPoints", 150.75);
viewSegmentation.Add("HasMagicStaff", true);
viewSegmentation.Add("CastingSpeed", 1.1f);
viewSegmentation.Add("CharacterID", 9988776655L);
Countly.Instance.Views.StartView("View Name", viewSegmentation);
These views would also return a string view ID when they are called.
Stopping Views
You can stop a view with its name or its view ID. To stop it with its name:
Countly.Instance.Views.StopViewWithName("View Name");
You can provide a segmentation while doing so:
Dictionary<string, object> viewSegmentation = new Dictionary<string, object>();
viewSegmentation.Add("CardName", "Spell Pierce");
viewSegmentation.Add("ManaCost", 1);
viewSegmentation.Add("CardValue", 3.5);
viewSegmentation.Add("IsFoil", false);
viewSegmentation.Add("CardPower", 0.0f);
viewSegmentation.Add("CardID", 9876543210L);
Countly.Instance.Views.StopViewWithName("View Name", viewSegmentation);
If multiple views have the same name (they would have different identifiers), but if you try to stop one with that name, the SDK will close the one that started last.
To stop a view with its view ID:
Countly.Instance.Views.StopViewWithID("View ID");
You can provide a segmentation while doing so:
// starting view and recording view id
string id = Countly.Instance.Views.StartAutoStoppedView("View Name");
Dictionary<string, object> viewSegmentation = new Dictionary<string, object>();
viewSegmentation.Add("OpeningName", "Sicilian Defense");
viewSegmentation.Add("ECOCode", "B40");
viewSegmentation.Add("AverageGameDurationInMinutes", 45.5);
viewSegmentation.Add("IsAggressive", true);
viewSegmentation.Add("WinRatePercentage", 54.3f);
viewSegmentation.Add("OpeningID", 123456789L);
Countly.Instance.Views.StopViewWithID(id, viewSegmentation);
You can also stop all running views at once with a segmentation:
Dictionary<string, object> viewSegmentation = new Dictionary<string, object>();
viewSegmentation.Add("StationName", "Alpha Centauri Base");
viewSegmentation.Add("CrewCount", 120);
viewSegmentation.Add("DistanceFromEarth", 4.37E13);
viewSegmentation.Add("HasArtificialGravity", true);
viewSegmentation.Add("OrbitalPeriod", 365.25f);
viewSegmentation.Add("StationID", 2233445566L);
Countly.Instance.Views.StopAllViews(viewSegmentation);
Pausing and Resuming Views
If you start multiple views simultaneously, pausing some views while others continue might be necessary. You can achieve this by using the unique identifier you get when starting a view.
To pause a view with its ID:
Countly.Instance.Views.PauseViewWithID("View ID");
To resume a view with its ID:
Countly.Instance.Views.ResumeViewWithID("View ID");
Adding Segmentation to Started Views
You can add segmentation values to a view before it ends. This can be done as often as desired, and the final segmentation sent to the server will be the cumulative sum of all segmentations. However, if a certain segmentation value for a specific key has been updated, the latest value will be used.
To add segmentation to a view using its view ID:
string id = Countly.Instance.Views.StartView("View Name");
Dictionary<string, object> viewSegmentation = new Dictionary<string, object>();
viewSegmentation.Add("BookTitle", "Dune");
viewSegmentation.Add("Author", "Frank Herbert");
viewSegmentation.Add("PublicationYear", 1965);
viewSegmentation.Add("NumberofPages", 412);
viewSegmentation.Add("IsScienceFiction", true);
viewSegmentation.Add("AverageRating", 4.35f);
Countly.Instance.Views.AddSegmentationToViewWithID(id, viewSegmentation);
To add segmentation to a view using its name:
string viewName = "View Name";
Countly.Instance.Views.StartView(viewName);
Dictionary<string, object> viewSegmentation = new Dictionary<string, object>();
viewSegmentation.Add("FavoritePet", "Unicorn");
viewSegmentation.Add("PetAge", 1);
viewSegmentation.Add("PetMagicLevel", 100.0);
viewSegmentation.Add("IsMythical", true);
viewSegmentation.Add("HeightInMeters", 2.5f);
viewSegmentation.Add("PetID", 1234567890L);
Countly.Instance.Views.AddSegmentationToViewWithName(viewName, viewSegmentation);
Global-View Segmentation
You can set a global segmentation to be sent with all views when it ends:
Dictionary<string, object> viewSegmentation = new Dictionary<string, object>();
viewSegmentation.Add("DeskPlant", "Cactus");
viewSegmentation.Add("CoffeeMugsOwned", 7);
viewSegmentation.Add("AverageMeetingLength", 1.5);
viewSegmentation.Add("HasOfficeDog", true);
viewSegmentation.Add("DeskHeight", 1.2f);
viewSegmentation.Add("EmployeeID", 1122334455L);
Countly.Instance.Views.SetGlobalViewSegmentation(viewSegmentation);
You can update this segmentation any time you want:
Dictionary<string, object> viewSegmentation = new Dictionary<string, object>();
viewSegmentation.Add("FavoriteGenre", "Sci-Fi");
viewSegmentation.Add("MoviesWatchedThisMonth", 15);
viewSegmentation.Add("AverageRating", 4.8);
viewSegmentation.Add("HasStreamingSubscription", true);
viewSegmentation.Add("ScreenSize", 55.0f);
viewSegmentation.Add("UserID", 5566778899L);
Countly.Instance.Views.UpdateGlobalViewSegmentation(viewSegmentation);
Consent
This feature requires Views
consent. No additional views will be recorded if consent is required and not given.
Device ID Management
A device ID is a unique identifier for your users. You may specify the device ID yourself or allow the SDK to generate it. When providing one yourself, keep in mind that it has to be unique for all users. Some potential sources for such an id may be the users username, email or some other internal ID used by your other systems.
You can provide a device ID during initialization like this:
string DeviceId = "UNIQUE_DEVICE_ID";
CountlyConfiguration config = new CountlyConfiguration(appKey, serverUrl)
.SetDeviceId(DeviceId);
Countly.Instance.Init(config);
Retrieving Current Device ID
You may want to see what device id Countly is assigning for the specific device. For that, you may use the following calls.
string usedId = Countly.Instance.Device.DeviceId;
You can get the current device ID type. The id type is an enum with the possible values of:
- SDKGenerated - device ID generated by the SDK
- DeveloperProvided - device ID provided by the host app
DeviceIdType type = Countly.Instance.Device.DeviceIdType;
Changing Device ID
The SDK allows you to change the Device ID at any point in time. You can use any of the following two methods to changing the Device ID, depending on your needs.
Performance risk. Changing device id with server merging results in huge load on server as it is rewriting all the user history. This should be done only once per user.
Changing Device ID with Server Merge
In case your application authenticates users, you might want to change the ID to the one in your backend after he has logged in. This helps you identify a specific user with a specific ID on a device he logs in, and the same scenario can also be used in cases this user logs in using a different way (e.g another tablet, another mobile phone, or web). In this case, 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:
await Countly.Instance.Device.ChangeDeviceIdWithMerge("New Device Id");
Changing Device ID without Server Merge
You might want to track information about another separate user that starts using your app (changing apps account), or your app enters a state where you no longer can verify the identity of the current user (user logs out). In that case, you can change the current device ID to a new one without merging their data. You would call:
await Countly.Instance.Device.ChangeDeviceIdWithoutMerge("New Device Id");
Doing it this way, will not merge the previously acquired data with the new id.
If device ID is changed without merging and consent was enabled, all previously given consent will be removed. This means that all features will cease to function until new consent has been given again for that new device ID.
Do note that every time you change your deviceId without a merge, it will be interpreted as a new user. Therefore implementing id management in a bad way could inflate the users count by quite a lot.
Device ID Generation
If no device ID is provided the first time the SDK is initialised, the SDK will generate a unique device ID. The source of that id isSystemInfo.deviceUniqueIdentifier
which is a value exposed by Unity. It should be unique for every device.
Here are the underlying mechanisms used to generate that value for some platforms:
IOS: on pre-iOS7 devices, it will return a hash of the MAC address. On iOS7 devices, it will be
UIDevice identifierForVendor
or, if that fails for any reason,
ASIdentifierManager advertisingIdentifier
Android: SystemInfo.deviceUniqueIdentifier
returns the md5 of ANDROID_ID. Note that since Android 8.0 (API level 26) ANDROID_ID depends on the app signing key. That means "unsigned" builds (which are by default signed with a debug keystore) will have a different value than signed builds (which are signed with a key provided in the player settings).
Windows Store Apps: uses AdvertisingManager::AdvertisingId
for returning unique device identifiers.
Windows Standalone: returns a hash from the concatenation of strings taken from Computer System Hardware Classes. For more information, click here.
Consent
No consent is required to change device ID.
If device ID is changed without merging and consent was enabled, all previously given consent will be removed. This means that all features will cease to function until new consent has been given again for that new device ID.
Push Notifications
The Unity SKD uses FCM and APNs as push notification providers for Android and iOS platforms respectively, and it doesn't support the Huawei Push Kit push service.
By default, FCM and APNs dependencies are added as part of the SDK. They can be removed in case you don't need them.
Note that SDK doesn't support Deep linking, Data only push, and Rich push notifications yet. You can send text push notifications only.
Integration
Android
The Countly server needs an FCM server key to send notifications through FCM.
To set it up, refer to Android documentation and follow the following steps:
- Download google-services.json from Firebase console.
- Create google-services.xml from google-services.json. You can use an online converter here.
- Put your file google-services.xml in /Plugins/Android/Notifications/res/values (replace if necessary).
iOS
The Countly server needs the APNs Auth Key to send notifications. To get the APNs Auth Key and upload it to the County Server, for further information refer to iOS Documentation.
To set up push for iOS, follow the next two steps:
1. In Unity, go to Player Settings. In the Other Settings section, add the "COUNTLY_ENABLE_IOS_PUSH" symbol in Scripting Define Symbols.
2. After exporting the iOS project, open the project in Xcode, and add Push Notifications Capability. For further information regarding iOS app configuring refer to iOS Documentation.
Enabling Push
By default Push Notifications are disabled. To enable push, set Notification mode other than None
in the Configuration before SDK init call.
Example:
CountlyConfiguration configuration = new CountlyConfiguration(appKey, serverUrl)
.SetNotificationMode(TestMode.AndroidTestToken);
Here is an overview of notification modes:
None
- it is the default value of notification mode. This mode disables the notification feature.AndroidTestToken
/iOSTestToken
- during development build, useAndroidTestToken
andiOSTestToken
modes for Android and iOS platforms respectively.iOSAdHocToken
- use this for distribution of iOS builds on TestFlight and AdHoc.-
ProductionToken
- use this mode for the production builds.
Removing Push and Its Dependencies
By default, push dependencies are part of the SDK. You may remove them, and add them back after removing them. Don't forget to change notification mode to None
, after removing push notification dependencies from SDK.
Android
To remove FCM dependencies from the android build, go to the Assets\Plugins\Android folder and delete the Notifications folder.
To add them back after removing, re-import the Unity package.
IOS
The APN's dependencies are part of the SDK. To remove the APNs dependencies, go to the Assets\Plugins folder and delete the iOS folder. Remove the "COUNTLY_ENABLE_IOS_PUSH" symbol from Scripting Define Symbols in Player Settings.
To add them back after removing, re-import the Unity package and add back the "COUNTLY_ENABLE_IOS_PUSH" symbol.
Customizing Push Messages
Android
To change the sound and icons of the android notifications, update the sound and icons in the folder Assets/Plugins/Android/Notifications/res.
Note: The Notification channel name and description can be updated through the strings.xml file located in the Assets\Plugins\Android\Notifications\res\values folder.
Handling Push Callbacks
In order to listen to notification receive and click events, implement INotificationListener
interface and its members' methods OnNotificationClicked
and OnNotificationReceived
into your class.
Example:
public class CountlyEntryPoint : MonoBehaviour, INotificationListener
{
public void OnNotificationReceived(string message)
{
}
public void OnNotificationClicked(string message, int index)
{
}
}
There are two ways to register for this class to listen to notification events.
1. You may call AddListener(this)
on CountlyConfiguration
object before the SDK Init call.
Example:
private void Awake()
{
CountlyConfiguration config = new CountlyConfiguration(appKey, serverUrl);
config.AddNotificationListener(this);
Countly.Instance.Init(config);
}
2. If SDK has been initialized, use the following code snippet to listen to push notification events.
Countly.Instance.Notifications.AddListener(this);
To stop listening notification receive and click events, call
Countly.Instance.Notifications.RemoveListener(this);
For more information, check the sample app on GitHub.
Consent
This feature requiresPush
consent. No push notifications will be received if consent is required and not given.
User Location
While integrating this SDK into your application, you might want to track your user location. You could use this information to better know your app’s 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).
- Latitude and longitude values separated by a comma, e.g. "56.42345,123.45325".
- Your user’s IP address.
Setting Location
During init, you can set location info in the configuration:
config.SetLocation(countryCode, city, gpsCoordinates, ipAddress);
After SDK initialization, this location info will be sent to the server at the start of the user session.
Note that the IP address will only be updated if set through the init process.
Use Countly.Location.
to disable or set the location at any time after the SDK Init call.
For example:
string countryCode = "us";
string city = "Houston";
string latitude = "29.634933";
string longitude = "-95.220255";
string ipAddress = null;
Countly.Instance.Location.SetLocation(countryCode, city, latitude + "," + longitude, ipAddress);
When those values are set, a separate request will be created to send them. Except for IP address, because Countly server processes IP address only when starting a session.
If you don't want to set specific fields, set them to null.
Disabling Location
Users might want to opt-out of location tracking. To do so, you can disable location during init:
config.DisableLocation();
To disable location after SDK initialization, call:
Countly.Instance.Location.DisableLocation();
These actions will erase the cached location data from the device and the server.
Consent
This feature requiresLocation
consent. If consent is not given and is required, no location information will be recorded. No reverse geo IP will be performed server side. SDK will behave as if location tracking is disabled.
If consent was given and then was removed, it will create a request that will clear location information server side.
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 Countly.Instance.RemoteConfigs.Update()
. After the successful download, the SDK stores the updated config locally.
await Countly.Instance.RemoteConfigs.Update();
Accessing Remote Config Values
To access the stored config, call Countly.Instance.RemoteConfigs.Configs
. It will return null
if there isn't any config stored.
Dictionary<string, object> config = Countly.Instance.RemoteConfigs.Configs;
The Dictionary<string, object>
returns a value of the type object
against a key. The developer then needs to cast it to the appropriate type.
Consent
This feature requiresRemoteConfig
consent. If consent is required and not given, no remote config information will be downloaded and stored.
If consent was given and then is removed, locally stored remote config information will be cleared.
User Feedback
Star Rating Dialog
When a user rates your application, you can report it to the Countly server.
Example:
await Countly.Instance.StarRating.ReportStarRatingAsync(platform: "android", appVersion: "0.1", rating: 3);
All parameters are mandatory.
- platform - (string) the name of the platform.
- appVersion - (string) the current version of the app.
- rating - (int) value from 0 to 5 that will be set as the rating value.
Consent
If consent is required, recording Star Rating requiresStarRating
consent. If consent is required and not given, recording a Star Rating will not be possible.
User Profiles
The User Profiles feature is available in Countly Enterprise and built-in Flex.
User Profiles is a tool for identifying users, their devices, event timelines, and application crash information. It may contain any information you collect or that is collected automatically by the Countly SDK.
You may send user-related information to Countly and let the Countly Dashboard show and segment this data. You may also send a notification to a group of users. For more information about User Profiles, review this documentation
Setting User Properties
In the SDK, the typical workflow involves using the following methods to provide information about the current user:
// Provide multiple properties at once within a dictionary
Countly.Instance.UserProfile.SetProperties(Dictionary <string, object> userProperties);
// Provide single user property as key and value
Countly.Instance.UserProfile.SetProperty(string key, object value);
These methods allow you to set predefined fields or any custom fields you wish to include. While saving User Profile data by calling Countly.UserProfile.Save()
is not mandatory, if required manually saving User Profile data by that call can still be applied. Recorded User Profile data is automatically sent when:
- An event is recorded
- A session update occurs
- The device ID changes
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 | int | User's year of birth as integer |
Using "" for strings or a negative number for 'byear' will effectively delete that property.
You may use any key values to store and display on your Countly backend for custom user properties. Note: Keys with . or $ symbols will have those symbols removed.
Recording Custom Values
Both methods can be used to record custom User Profile data. Example usage would be:
// Record the User Profile data by SetProperty method Countly.Instance.UserProfile.SetProperty("ExampleKey", "ExampleValue"); // Send recorded value to the server manually if needed Countly.Instance.UserProfile.Save(); // Create a Dictionary for SetProperties method Dictionary<string, object> userProperties = new Dictionary<string, object> { { "ExampleKey2", "ExampleValue2" } }; // Record the User Profile data by SetProperties method Countly.Instance.UserProfile.SetProperties(userProperties); // Send recorded value to the server manually if needed Countly.Instance.UserProfile.Save();
Recording Predefined Values
In the same way as recording custom User Profile data, both methods can be used again to record predefined User Profile data. Example usage would be:
// Record single User Profile data by SetProperty method
Countly.Instance.UserProfile.SetProperty("name", "Albert Einstein");
// Send recorded value to the server manually if needed
Countly.Instance.UserProfile.Save();
// Create a Dictionary containing User Profile data
Dictionary<string, object> userProperties = new Dictionary<string, object>
{
{ "name", "Albert Einstein" },
{ "username", "albert" },
{ "email", "info@albert.einstein" },
{ "organization", "Theoretical Physics Institute" },
{ "phone", "90 123 456 7890" },
{ "picture", "https://ExamplePictureUrl.org/geniuses/Albert_Einstein.jpg" },
{ "gender", "M" },
{ "byear", 1879 }
};
// Record the User Profile data by SetProperties method
Countly.Instance.UserProfile.SetProperties(userProperties);
// Send recorded value to the server manually if needed
Countly.Instance.UserProfile.Save();
It's also possible to record custom and predefined data within the same dictionary in a single call. Example usage would be:
// Record both custom and predefined values
Dictionary<string, object> userProperties = new Dictionary<string, object>
{
// User values
{ "name", "Marie Curie" },
{ "username", "marie" },
{ "email", "info@marie.curie" },
{ "organization", "Institute of Radium" },
{ "phone", "90 987 654 3210" },
{ "picture", "https://ExamplePictureUrl.org/geniuses/Marie_Curie.jpg" },
{ "gender", "F" },
{ "byear", 1867 },
// Custom values
{ "fieldOfStudy", "Radioactivity" },
{ "nobelPrizes", new List { "Physics 1903", "Chemistry 1911" } },
{ "discovery", "Polonium and Radium" }
};
// Record the values with SetProperties call
Countly.Instance.UserProfile.SetProperties(userProperties);
// Send recorded value to the server manually if needed
Countly.Instance.UserProfile.Save();
Setting User Picture
As mentioned above SDK allows you to set the user's picture URL along with other details using both SetProperties
and SetProperty
methods.
Example usage would be:
// Set User Picture with SetProperty method
Countly.Instance.UserProfile.SetProperty("picture", "https://ExamplePictureUrl.org/geniuses/Richard_Garfield.jpg");
// Send recorded value to the server manually if needed
Countly.Instance.UserProfile.Save();
//Create a dictionary that contains picture URL data
Dictionary<string, object> userProperties = new Dictionary<string, object>
{
{ "picture", "https://ExamplePictureUrl.org/geniuses/Marshall_Mathers.jpg" }
};
// Set the user profile picture using the SetProperties method
Countly.Instance.UserProfile.SetProperties(userProperties);
// Send recorded value to the server manually if needed
Countly.Instance.UserProfile.Save();
Modifying Data
You may also manipulate your custom data values in different ways, such as incrementing the current value on a server or storing an array of values under the same property.
You will find the list of available manipulations below:
// Increment custom property value by 1
Countly.Instance.UserProfile.Increment("used");
// Increment custom property value by provided value
Countly.Instance.UserProfile.IncrementBy("used", 2);
// Save maximal value between existing and provided
Countly.Instance.UserProfile.SaveMax("highscore", 300);
// Save minimal value between existing and provided
Countly.Instance.UserProfile.SaveMin("best_time", 60);
// Multiply custom property value by the provided value
Countly.Instance.UserProfile.Multiply("used", 3);
// Removes existing property from the array
Countly.Instance.UserProfile.Pull("type", "remove");
// Insert value to the array, which can have duplicates
Countly.Instance.UserProfile.Push("force", "add");
// Insert value to the array of unique values
Countly.Instance.UserProfile.PushUnique("single", "unique");
// Set value if it does not exist yet
Countly.Instance.UserProfile.SetOnce("tag", "test");
// Send provided values to server
Countly.Instance.UserProfile.Save();
Apart from updating a single property in one request, modifying multiple (unique) properties in one request is possible. For example, this enables incrementing HighScore and multiplying BestTime in the same request. Similarly, it's possible to record any number of modified requests and save them all together in one single request instead of multiple requests.
It should be noted that when modifying multiple properties in one request, the properties must be unique. A property shouldn’t be modified more than once in a single request. However, if a property is recorded more than once, only the latest value will be posted to the server.
Example:
Countly.Instance.UserProfile.IncrementBy("HighScore", 90);
Countly.Instance.UserProfile.Multiply("BestTime", 20);
Countly.Instance.UserProfile.Save();
Consent
This feature requiresUsers
consent. If consent is required and not given, recording user profile information will not be possible.
User Consent
In an effort to comply with GDPR, starting from 20.11.1, Unity Countly SDK provides ways to toggle different Countly features on/off depending on the given consent.
More information about GDPR can be found here.
Setup During Init
The requirement for consent is disabled by default. To enable it, you will have to set RequiresConsent
value true
before initializing Countly.
CountlyConfiguration configuration = new CountlyConfiguration(appKey, serverUrl)
.EnableLogging()
.SetNotificationMode(TestMode.AndroidTestToken)
.SetRequiresConsent(true);
Countly.Instance.Init(configuration);
By default, when consent is required, no consent is given. If no consent is given, SDK will not work and no network requests related to its features will be sent. When the consent status of a feature is changed, that change will be sent to the Countly server.
Set consent is not persistent and will have to be set each time before Countly init. Therefore, the storage and persistence of the given consent fall on the SDK integrator.
Consent for features may be given and revoked at any time, but if it is given after Countly init, some features may only work in part.
Feature names in the Unity SDK, are stored as Enum called Consents
.
The current features are:
-
Sessions
- tracking when, how often, and how long users use your app -
Events
- allow sending events to the server -
Views
- allow the tracking of which views users visit -
Location
- allow the sending of location information -
Crashes
- allow the tracking of crashes, exceptions, and errors -
Users
- allow the collecting/providing of user information, including custom properties -
Push
- allow push notifications -
StarRating
- allow their rating and feedback to be sent -
RemoteConfig
- allow downloading remote config values from your server
In case consent is required, you may give consent to features before the SDK Init call. These features consents are not persistent and must be given on every restart.
// prepare consents that should be given
Consents[] consents = new Consents[] { Consents.Users, Consents.Location };
// give consents to the features
configuration.GiveConsent(consents);
Changing Consent
After init call, use Countly.Instance.Consents.
to change consent.
There are 2 ways of changing feature consent:
-
GiveConsent
/RemoveConsent
- gives or removes consent to a specific feature.
// give consent to "sessions" feature
Countly.Instance.Consents.GiveConsent(new Consents[] { Consents.Sessions });
// remove consent from "sessions" feature
Countly.Instance.Consents.RemoveConsent(new Consents[] { Consents.Sessions });
-
GiveConsentAll
/RemoveAllConsent
- gives or removes all consents.
// give consent to all features
Countly.Instance.Consent.GiveConsentAll();
// remove consent from all features
Countly.Instance.Consent.RemoveAllConsent();
Feature Groups
Consents may be put into groups. By doing this, you may give/remove consent to multiple features in the same call. Groups may be created using CreateConsentGroup
call during SDK configuration. Those groups are not persistent and must be created on every restart. During SDK configuration consents to groups may be given by using GiveConsentToGroup
.
// prepare consents that should be added to the group
Consents[] consents = new Consents[] { Consents.Users, Consents.Location };
// create the Consent group
configuration.CreateConsentGroup("User-Consents", consents);
// give consent to the provide consent group
configuration.GiveConsentToGroup("User-Consents");
After init has been called, use GiveConsentToGroup
/ RemoveConsentOfGroup
to give or remove consent for a feature group.
Example:
// prepare array of groups
string[] groupName = new string[] { "User-Consents", "Events-Consents" };
// give consent to groups
Countly.Instance.Consent.GiveConsentToGroup(groupName);
// remove consent of groups
Countly.Instance.Consent.RemoveConsentOfGroup(groupName);
Security and Privacy
Parameter Tamper Protection
You may set the 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 exactly the 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
field before being processed.
CountlyConfiguration configuration = new CountlyConfiguration(appKey, serverUrl)
.SetParameterTamperingProtectionSalt("Salt");
Other Features
SDK Config Parameters Explained
To change the Configuration, update the values of parameters in the "CountlyConfiguration" object. These are the methods that lets you set values in your "CountlyConfiguration" object:
SetDeviceId(string deviceId) - your Device ID. It is an optional parameter. Example: f16e5af2-8a2a-4f37-965d-qwer5678ui98.
SetParameterTamperingProtectionSalt(string salt) - used to prevent parameter tampering. The default value is NULL.
EnableForcedHttpPost() - when enabled, 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
SetRequiresConsent(bool enable) - this is useful during the app run when the user wants to opt-out of SDK features.
EnableLogging() - this parameter is useful when you are debugging your application. When set to true, it basically turns on Logging.
SetUpdateSessionTimerDelay(int duration) - sets the interval (in seconds) after which the application will automatically extend the session, providing the manual session is disabled. This interval is also used to process requests in the queue. The default value is 60 (seconds).
SetEventQueueSizeToSend(int threshold) - sets a threshold value that limits the number of events that can be recorded internally by the system before they can all be sent together in one request. Once the threshold limit is reached, the system groups all recorded events and sends them to the server. The default value is 100.
SetMaxRequestQueueSize(int limit) - sets a threshold value that limits the number of requests that can be stored internally by the system. The system processes these requests after every session duration interval has passed. The default value is 1000.
SetNotificationMode(TestMode mode) - when None, the SDK disables Push Notifications for the device. Use an iOS Test Token or an Android Test Token for testing purposes, and in production use a Production Token. The SDK uses the supplied mode for sending Push Notifications. The default value is None.
DisableAutomaticCrashReporting() - turns off Automatic Crash Reporting. When enabled, the SDK will catch exceptions and automatically report them to the Countly server. It's enabled by default.
Example Integrations
To look at our sample application, download the sample project from GitHub repo and open the 'EntryPoint.unity' scene. 'EntryPoint.unity' located in 'Example' folder under Assets. There is also 'CountlyEntryPoint.cs' script in Example folder, and this script shows how most of the functionality can be used.
Checking If the SDK Has Been Initialized
In case you would like to check if init has been called, you may use the following property:
Countly.Instance.IsSDKInitialized;
SDK Internal Limits
SDK does have configurable fields to manipulate the internal SDK value and key limits. If values or keys provided by the user, would exceed the limits, they would be truncated. These are the methods that let's you set limits. For further details please have a look here.
Key Length
SetMaxKeyLength(int length) - maximum size of all string keys. The default value is 128.
CountlyConfiguration configuration = new CountlyConfiguration(appKey, serverUrl)
.SetMaxKeyLength(120);
Value Size
SetMaxValueSize(int size) - maximum size of all values in our key-value pairs. The default value is 256.
CountlyConfiguration configuration = new CountlyConfiguration(appKey, serverUrl)
.SetMaxValueSize(240);
Segmentation Values
SetMaxSegmentationValues(int values) - maximum amount of custom (dev provided) segmentation in one event. The default value is 100.
CountlyConfiguration configuration = new CountlyConfiguration(appKey, serverUrl)
.SetMaxSegmentationValues(35);
Breadcrumb Count
SetMaxBreadcrumbCount(int amount) - maximum amount of breadcrumbs. The default value is 100.
CountlyConfiguration configuration = new CountlyConfiguration(appKey, serverUrl)
.SetMaxBreadcrumbCount(101);
Stack Trace Lines Per Thread
SetMaxStackTraceLinesPerThread(int lines) - limits how many stack trace lines would be recorded per thread. The default value is 30.
CountlyConfiguration configuration = new CountlyConfiguration(appKey, serverUrl)
.SetMaxStackTraceLinesPerThread(32);
Stack Trace Line Length
SetMaxStackTraceLineLength(int length) - limits how many characters are allowed per stack trace line. The default value is 200.
CountlyConfiguration configuration = new CountlyConfiguration(appKey, serverUrl)
.SetMaxStackTraceLineLength(230);
Custom Metrics
In certain situations, such as beginning a session or requesting remote config, the SDK sends device metrics. You have the flexibility to override the sent metrics, such as the operating system for a specific variant, or to provide your own custom metrics by using SetMetricOverride
.
Example:
// overriding default metrics
Dictionary<string, string> overridenMetrics = new Dictionary<string, string>();
overridenMetrics.Add("_os", "CustomOS");
configuration.SetMetricOverride(overridenMetrics);
// providing custom metrics
Dictionary<string, string> customMetric = new Dictionary<string, string>();
customMetric.Add("customMetric", "CustomValue");
configuration.SetMetricOverride(customMetric);
For more information about metric keys, you can refer here for a comprehensive list and descriptions of available metrics.
Setting Event Queue Threshold
In SDK configuration, you may limit the number of events that can be recorded internally by the system before they can all be sent together in one request. Example:
CountlyConfiguration configuration = new CountlyConfiguration(appKey, serverUrl)
.SetEventQueueSizeToSend(1222);
Countly.Instance.Init(configuration);
Once the threshold limit is reached, the system groups all recorded events and sends them to the server.
Setting Maximum Request Queue Size
When you initialize Countly, you can specify a value for the StoredRequestLimit flag. This flag limits the number of requests that can be stored in the request queue when the Countly server is unavailable or experiencing connection problems.
If the server is down, requests sent to it will be queued on the device. If the number of queued requests becomes excessive, it can cause problems with delivering the requests to the server, and can also take up valuable storage space on the device. To prevent this from happening, the StoredRequestLimit flag limits the number of requests that can be stored in the queue.
If the number of requests in the queue reaches the StoredRequestLimit limit, the oldest requests in the queue will be dropped, and the newest requests will take their place. This ensures that the queue doesn't become too large, and that the most recent requests are prioritized for delivery.
If you do not specify a value for the StoredRequestLimit flag, the default setting of 1,000 will be used.
CountlyConfiguration configuration = new CountlyConfiguration(appKey, serverUrl)
.SetMaxRequestQueueSize(500);
FAQ
What Information is Collected by the SDK
The following description mentions 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. For further information please have a look here.
- When generating a device ID, if no custom ID is provided, the SDK will use:
- Android: md5 of ANDROID_ID
- iOS: It will be vendor id and advertising id as a fallback
- Windows Store Apps: It will be advertising id
- Windows Standalone: It will be hash from the concatenation of strings taken from computer system hardware classes.