Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
## 0.2.6
- Added support for setting a "ResponseListener" via Aptabase.SetResponseListener. This allows you to receive callbacks with the HttpStatusCode for each event sent
- Added support for enabling or disabling the SDK via Aptabase.SetEnabled. This starts/stops polling as well.

## 0.2.5
- Reduced memory allocations by utilizing ListPool and DictionaryPool
- Flush method properly returns a cancellable Task
- CancellationToken handled all the way through to the WebRequest
- Improved error handling and logging

## 0.2.4

- Fixed memory leak with event errors
Expand Down
17 changes: 16 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,26 @@ Aptabase.TrackEvent("app_started", new Dictionary<string, object>
});
```

If you want to manually flush the event queue you can use
If you want to manually flush the event queue you can use:
```csharp
await Aptabase.Flush();
```
or
```csharp
Aptabase.Flush();
```

If you want to react to HttpStatusCodes received from the server, you can use:
```csharp
Aptabase.SetResponseListener((statusCode) => UnityEngine.Debug.Log($"Aptabase response status code: {statusCode}"));
```

If you want to enable or disable the SDK (note: also starts/stops polling), you can use:
```csharp
Aptabase.SetEnabled(enabled);
```
It defaults to **enabled**.

A few important notes:

1. The SDK will automatically enhance the event with some useful information, like the OS, the app version, and other things.
Expand Down
113 changes: 68 additions & 45 deletions Runtime/Aptabase.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
using System;
using System.Collections.Generic;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.Pool;
using Random = UnityEngine.Random;

namespace AptabaseSDK
Expand All @@ -13,99 +15,115 @@ public static class Aptabase
private static IDispatcher _dispatcher;
private static EnvironmentInfo _env;
private static Settings _settings;

private static DateTime _lastTouched = DateTime.UtcNow;
private static string _baseURL;

private static readonly TimeSpan _sessionTimeout = TimeSpan.FromMinutes(60);

private static readonly Dictionary<string, string> _hosts = new()
{
{ "US", "https://us.aptabase.com" },
{ "EU", "https://eu.aptabase.com" },
{ "DEV", "http://localhost:3000" },
{ "SH", "" },
{ "SH", "" }
};


private static bool _isEnabled = true;
private static int _flushTimer;
private static CancellationTokenSource _pollingCancellationTokenSource;

[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
private static void Initialize()
{
//load settings
// load settings
_settings = Resources.Load<Settings>("AptabaseSettings");
if (_settings == null)
{
Debug.LogWarning("Aptabase Settings not found. Tracking will be disabled");
Debug.LogError("[AptabaseAnalytics] Aptabase Settings not found. Tracking will be disabled");
return;
}

var key = _settings.AppKey;

var parts = key.Split("-");
if (parts.Length != 3 || !_hosts.ContainsKey(parts[1]))
{
Debug.LogWarning($"The Aptabase App Key {key} is invalid. Tracking will be disabled");
Debug.LogError($"[AptabaseAnalytics] The Aptabase App Key {key} is invalid. Tracking will be disabled");
return;
}

_env = Environment.GetEnvironmentInfo(Version.GetVersionInfo(_settings));

_baseURL = GetBaseUrl(parts[1]);

#if UNITY_WEBGL
_dispatcher = new WebGLDispatcher(_settings.AppKey, _baseURL, _env);
#else
_dispatcher = new Dispatcher(_settings.AppKey, _baseURL, _env);
#endif

//create listener
if (string.IsNullOrEmpty(_baseURL))
return;

#if UNITY_WEBGL
_dispatcher = new WebGLDispatcher(_settings.AppKey, _baseURL, _env);
#else
_dispatcher = new Dispatcher(_settings.AppKey, _baseURL, _env);
#endif

// create listener
var eventFocusHandler = new GameObject("AptabaseService");
eventFocusHandler.AddComponent<AptabaseService>();
}

private static async void StartPolling(int flushTimer)

public static void SetResponseListener(Action<HttpStatusCode> onResponse)
{
if (_dispatcher == null)
{
Debug.LogError("[AptabaseAnalytics] Aptabase is not initialized. Please check your settings.");
return;
}

_dispatcher.SetResponseListener(onResponse);
}

public static void SetEnabled(bool enabled)
{
_isEnabled = enabled;

if (!enabled)
StopPolling();
else
_ = StartPolling(GetFlushInterval());
}

private static async Task StartPolling(int flushTimer)
{
StopPolling();

_flushTimer = flushTimer;
_pollingCancellationTokenSource = new CancellationTokenSource();

while (_pollingCancellationTokenSource is { IsCancellationRequested: false })
{
try
{
await Task.Delay(_flushTimer, _pollingCancellationTokenSource.Token);
Flush();
await Flush();
}
catch (TaskCanceledException)
{
break;
}
}
}

private static void StopPolling()
{
if (_flushTimer <= 0)
return;

_pollingCancellationTokenSource?.Cancel();
_pollingCancellationTokenSource?.Dispose();
_pollingCancellationTokenSource = null;
_flushTimer = 0;
}

public static void OnApplicationFocus(bool hasFocus)
{
if (hasFocus)
{
StartPolling(GetFlushInterval());
}
else
{
Flush();
StopPolling();
}
if (_isEnabled)
_ = hasFocus ? StartPolling(GetFlushInterval()) : Flush().ContinueWith(_ => StopPolling());
}

private static string EvalSessionId()
Expand All @@ -118,14 +136,15 @@ private static string EvalSessionId()
_lastTouched = now;
return _sessionId;
}

private static string GetBaseUrl(string region)
{
if (region == "SH")
{
if (string.IsNullOrEmpty(_settings.SelfHostURL))
{
Debug.LogWarning("Host parameter must be defined when using Self-Hosted App Key. Tracking will be disabled.");
Debug.LogWarning(
"[AptabaseAnalytics] Host parameter must be defined when using Self-Hosted App Key. Tracking will be disabled.");
return null;
}

Expand All @@ -135,33 +154,37 @@ private static string GetBaseUrl(string region)
return _hosts[region];
}

public static void Flush()
public static Task Flush(CancellationToken cancellationToken = default)
{
_dispatcher.Flush();
return _dispatcher.Flush(cancellationToken);
}

public static void TrackEvent(string eventName, Dictionary<string, object> props = null)
public static void TrackEvent(string eventName, Dictionary<string, object> eventProps = null)
{
if (string.IsNullOrEmpty(_baseURL))
return;

props ??= new Dictionary<string, object>();
var eventData = new Event()

var props = DictionaryPool<string, object>.Get();
if (eventProps != null)
foreach (var prop in eventProps)
props.Add(prop.Key, prop.Value);

var eventData = new Event
{
timestamp = DateTime.UtcNow.ToString("o"),
sessionId = EvalSessionId(),
eventName = eventName,
systemProps = _env,
props = props
};

_dispatcher.Enqueue(eventData);
}

private static int GetFlushInterval()
{
if (_settings.EnableOverride && _settings.FlushInterval > 0)
return Mathf.Max(0, _settings.FlushInterval);
return _settings.FlushInterval;

return _env.isDebug ? 2000 : 60000;
}
Expand Down
Loading