Unreal Engine SDK - Data Privacy

Complying with Data Privacy Laws

Implement privacy-compliant data collection by notifying Singular of user consent choices for GDPR, CCPA, and other consumer privacy regulations.

When users consent or decline to share their information with third parties, use Singular's privacy methods to communicate their choice. This ensures compliance with regulations like California Consumer Privacy Act (CCPA) and enables partners to respect user privacy preferences.

Learn More: See User Privacy and Limit Data Sharing for detailed information on how Singular processes privacy consent.


Limit Data Sharing

Control Third-Party Data Sharing

Notify Singular whether users have consented to share their personal data with third-party partners using the LimitDataSharing method.

Method Signature:

static void LimitDataSharing(bool shouldLimitDataSharing)

Parameters:

  • false: User has opted in and consented to share their data
  • true: User has opted out and does not consent to share their data

Important: While optional, this method affects attribution data sharing. Some partners only share complete attribution information when explicitly notified that users have opted in.

Usage Examples

C++
// User has opted in to share their data
void AYourGameMode::OnUserOptedInToDataSharing()
{
    USingularSDKBPLibrary::LimitDataSharing(false);
    UE_LOG(LogTemp, Log, TEXT("Data sharing enabled"));
}

// User has opted out and declined to share their data
void AYourGameMode::OnUserOptedOutOfDataSharing()
{
    USingularSDKBPLibrary::LimitDataSharing(true);
    UE_LOG(LogTemp, Log, TEXT("Data sharing limited"));
}

// Example: Set based on user preference
void AYourGameMode::HandlePrivacyConsent(bool bUserConsented)
{
    // Pass inverse: false = opted in, true = opted out
    USingularSDKBPLibrary::LimitDataSharing(!bUserConsented);

    UE_LOG(LogTemp, Log, TEXT("Data sharing: %s"), 
           bUserConsented ? TEXT("Enabled") : TEXT("Limited"));
}

How It Works:

Singular uses this setting in User Privacy Postbacks and passes it to partners who require it for regulatory compliance.


GDPR Compliance Methods

Manage user tracking consent and control SDK functionality to comply with GDPR (General Data Protection Regulation) and other privacy regulations.

Tracking Consent Management

TrackingOptIn

Record explicit user consent for tracking by sending a GDPR opt-in event to Singular servers.

Method Signature:

static void TrackingOptIn()

When to Use:

  • GDPR Compliance: Call when users explicitly consent to tracking in GDPR-regulated regions
  • Consent Recording: Marks users as having provided GDPR consent in Singular's systems
  • Default Behavior: Without this call, SDK continues tracking but doesn't specifically record consent
C++
// User accepted tracking consent
void AGDPRManager::OnUserAcceptedTracking()
{
    USingularSDKBPLibrary::TrackingOptIn();
    UE_LOG(LogTemp, Log, TEXT("User opted in to tracking"));
}

// Example: Call after consent dialog
void AGDPRManager::ShowGDPRConsentDialog()
{
    // Your consent dialog UI logic here
    // ...

    // If user accepts:
    OnUserAcceptedTracking();
}

Tracking Control Methods

StopAllTracking

Completely disable all SDK tracking activities for the current user on this device.

Method Signature:

static void StopAllTracking()

Critical Warning: This method permanently disables the SDK until ResumeAllTracking() is called. The disabled state persists across app restarts and can only be reversed programmatically.

Behavior:

  • Immediate Effect: Stops all tracking, event reporting, and data collection instantly
  • Persistent State: Remains disabled even after app closes and reopens
  • No Automatic Reset: Must explicitly call ResumeAllTracking() to re-enable
C++
// User declined all tracking
void ATrackingController::OnUserDeclinedTracking()
{
    USingularSDKBPLibrary::StopAllTracking();
    UE_LOG(LogTemp, Log, TEXT("All tracking stopped"));

    // Optionally store preference
    SaveBoolToConfig(TEXT("tracking_enabled"), false);
}

// Example: Handle user opt-out from settings menu
void ATrackingController::HandlePrivacySettingsChange(bool bTrackingEnabled)
{
    if (!bTrackingEnabled)
    {
        USingularSDKBPLibrary::StopAllTracking();
        UE_LOG(LogTemp, Log, TEXT("Privacy settings: Tracking disabled"));
    }
}

ResumeAllTracking

Re-enable tracking after it was stopped with StopAllTracking().

Method Signature:

static void ResumeAllTracking()

Use Cases:

  • Consent Change: User changes privacy preferences and opts back into tracking
  • Privacy Settings: User updates consent through app settings menu
  • Regional Compliance: Re-enable tracking when user moves to non-regulated regions
C++
// User opted back in to tracking
void ATrackingController::OnUserResumedTracking()
{
    USingularSDKBPLibrary::ResumeAllTracking();
    UE_LOG(LogTemp, Log, TEXT("Tracking resumed"));

    // Optionally update stored preference
    SaveBoolToConfig(TEXT("tracking_enabled"), true);
}

// Example: Handle consent update from settings
void ATrackingController::HandlePrivacySettingsChange(bool bTrackingEnabled)
{
    if (bTrackingEnabled)
    {
        USingularSDKBPLibrary::ResumeAllTracking();
        UE_LOG(LogTemp, Log, TEXT("Privacy settings: Tracking enabled"));
    }
}

IsAllTrackingStopped

Check whether tracking has been disabled for the current user.

Method Signature:

static bool IsAllTrackingStopped()

Returns:

  • true: Tracking is currently stopped via StopAllTracking()
  • false: Tracking is active (either never stopped or resumed)
C++
// Check current tracking status on startup
void APrivacySettingsUI::BeginPlay()
{
    Super::BeginPlay();
    UpdatePrivacyUI();
}

// Check current tracking status
bool APrivacySettingsUI::IsTrackingEnabled()
{
    return !USingularSDKBPLibrary::IsAllTrackingStopped();
}

// Example: Display privacy status in settings
FString APrivacySettingsUI::GetPrivacyStatusText()
{
    if (USingularSDKBPLibrary::IsAllTrackingStopped())
    {
        return TEXT("Tracking: Disabled");
    }
    else
    {
        return TEXT("Tracking: Enabled");
    }
}

// Example: Sync UI with tracking state
void APrivacySettingsUI::UpdatePrivacyUI()
{
    bool bIsStopped = USingularSDKBPLibrary::IsAllTrackingStopped();

    if (TrackingToggle != nullptr)
    {
        TrackingToggle->SetIsChecked(!bIsStopped);
    }

    if (StatusText != nullptr)
    {
        FString StatusString = bIsStopped ? TEXT("Stopped") : TEXT("Active");
        StatusText->SetText(FText::FromString(
            FString::Printf(TEXT("Current tracking state: %s"), *StatusString)
        ));
    }

    UE_LOG(LogTemp, Log, TEXT("Current tracking state: %s"), 
           bIsStopped ? TEXT("Stopped") : TEXT("Active"));
}

// Handle toggle change from UI
void APrivacySettingsUI::OnTrackingToggleChanged(bool bEnabled)
{
    if (bEnabled)
    {
        USingularSDKBPLibrary::ResumeAllTracking();
    }
    else
    {
        USingularSDKBPLibrary::StopAllTracking();
    }

    UpdatePrivacyUI();
}

Children's Privacy Protection

TrackingUnder13

Notify Singular that the user is under 13 years old to comply with COPPA (Children's Online Privacy Protection Act) and other child privacy regulations.

Method Signature:

static void TrackingUnder13()

Compliance Requirements:

  • COPPA Compliance: Required for apps that collect data from children under 13 in the United States
  • Age-Gated Content: Use when users identify themselves as under 13 during registration or age verification
  • Restricted Tracking: Limits data collection to comply with children's privacy protection laws
C++
// User identified as under 13
void ACOPPAManager::OnUserUnder13()
{
    USingularSDKBPLibrary::TrackingUnder13();
    UE_LOG(LogTemp, Log, TEXT("COPPA mode enabled for user under 13"));
}

// Example: Call after age verification
void ACOPPAManager::OnAgeVerified(int UserAge)
{
    if (UserAge < 13)
    {
        USingularSDKBPLibrary::TrackingUnder13();
        UE_LOG(LogTemp, Log, TEXT("COPPA restrictions applied"));

        // Also limit data sharing for children
        USingularSDKBPLibrary::LimitDataSharing(true);
    }
}

// Example: Age gate implementation
void AAgeGateWidget::OnAgeSubmitted(int UserAge)
{
    if (UserAge < 13)
    {
        // Apply COPPA protections
        USingularSDKBPLibrary::TrackingUnder13();
        USingularSDKBPLibrary::LimitDataSharing(true);

        UE_LOG(LogTemp, Warning, TEXT("Child user detected - privacy protections enabled"));

        // Show age-appropriate content
        ShowChildSafeContent();
    }
    else
    {
        // Standard tracking for users 13+
        UE_LOG(LogTemp, Log, TEXT("Adult user - standard tracking enabled"));
        InitializeStandardTracking();
    }
}

Important: Call this method as early as possible after determining the user is under 13, ideally during app initialization or immediately after age verification. This ensures all subsequent tracking respects children's privacy regulations.


Implementation Best Practices

Complete Privacy Management Example

Implement comprehensive privacy controls that respect user preferences and comply with regulations.

C++
// ComprehensivePrivacyManager.h
#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "ComprehensivePrivacyManager.generated.h"

UCLASS()
class YOURGAME_API AComprehensivePrivacyManager : public AActor
{
    GENERATED_BODY()

public:
    AComprehensivePrivacyManager();

protected:
    virtual void BeginPlay() override;

public:
    // Initialize privacy settings on app startup
    UFUNCTION(BlueprintCallable, Category = "Privacy")
    void InitializePrivacySettings();

    // User accepts tracking via consent dialog
    UFUNCTION(BlueprintCallable, Category = "Privacy")
    void OnUserAcceptedTracking();

    // User declines tracking
    UFUNCTION(BlueprintCallable, Category = "Privacy")
    void OnUserDeclinedTracking();

    // User updates data sharing preference
    UFUNCTION(BlueprintCallable, Category = "Privacy")
    void SetDataSharingEnabled(bool bEnabled);

    // Check if tracking is currently enabled
    UFUNCTION(BlueprintCallable, Category = "Privacy")
    bool IsTrackingEnabled();

    // Get current privacy status as readable text
    UFUNCTION(BlueprintCallable, Category = "Privacy")
    FString GetPrivacyStatus();

private:
    // Helper methods for config storage
    bool GetUserConsent();
    void SaveUserConsent(bool bConsent);
    bool GetDataSharingPreference();
    void SaveDataSharingPreference(bool bEnabled);

    // Config keys
    static const FString PREF_USER_CONSENT;
    static const FString PREF_DATA_SHARING;
};

// ComprehensivePrivacyManager.cpp
#include "ComprehensivePrivacyManager.h"
#include "SingularSDKBPLibrary.h"
#include "Kismet/GameplayStatics.h"

const FString AComprehensivePrivacyManager::PREF_USER_CONSENT = TEXT("privacy_user_consent");
const FString AComprehensivePrivacyManager::PREF_DATA_SHARING = TEXT("privacy_data_sharing");

AComprehensivePrivacyManager::AComprehensivePrivacyManager()
{
    PrimaryActorTick.bCanEverTick = false;
}

void AComprehensivePrivacyManager::BeginPlay()
{
    Super::BeginPlay();

    // Initialize privacy settings on app start
    InitializePrivacySettings();
}

void AComprehensivePrivacyManager::InitializePrivacySettings()
{
    bool bHasUserConsent = GetUserConsent();
    bool bAllowDataSharing = GetDataSharingPreference();

    // Apply stored tracking preference
    if (bHasUserConsent)
    {
        USingularSDKBPLibrary::TrackingOptIn();
        USingularSDKBPLibrary::ResumeAllTracking();
        UE_LOG(LogTemp, Log, TEXT("Privacy initialized: Tracking enabled with consent"));
    }
    else
    {
        USingularSDKBPLibrary::StopAllTracking();
        UE_LOG(LogTemp, Log, TEXT("Privacy initialized: Tracking disabled"));
    }

    // Set data sharing preference (inverse logic)
    USingularSDKBPLibrary::LimitDataSharing(!bAllowDataSharing);

    UE_LOG(LogTemp, Log, TEXT("Privacy initialized: consent=%s, sharing=%s"), 
           bHasUserConsent ? TEXT("true") : TEXT("false"),
           bAllowDataSharing ? TEXT("true") : TEXT("false"));
}

void AComprehensivePrivacyManager::OnUserAcceptedTracking()
{
    SaveUserConsent(true);

    USingularSDKBPLibrary::TrackingOptIn();
    USingularSDKBPLibrary::ResumeAllTracking();

    UE_LOG(LogTemp, Log, TEXT("User accepted tracking"));
}

void AComprehensivePrivacyManager::OnUserDeclinedTracking()
{
    SaveUserConsent(false);

    USingularSDKBPLibrary::StopAllTracking();

    UE_LOG(LogTemp, Log, TEXT("User declined tracking"));
}

void AComprehensivePrivacyManager::SetDataSharingEnabled(bool bEnabled)
{
    SaveDataSharingPreference(bEnabled);

    // Note: LimitDataSharing uses inverse logic
    // false = data sharing enabled, true = data sharing limited
    USingularSDKBPLibrary::LimitDataSharing(!bEnabled);

    UE_LOG(LogTemp, Log, TEXT("Data sharing: %s"), 
           bEnabled ? TEXT("Enabled") : TEXT("Limited"));
}

bool AComprehensivePrivacyManager::IsTrackingEnabled()
{
    return !USingularSDKBPLibrary::IsAllTrackingStopped();
}

FString AComprehensivePrivacyManager::GetPrivacyStatus()
{
    bool bIsEnabled = !USingularSDKBPLibrary::IsAllTrackingStopped();
    bool bDataSharingEnabled = GetDataSharingPreference();

    return FString::Printf(TEXT("Tracking: %s\nData Sharing: %s"),
                          bIsEnabled ? TEXT("Enabled") : TEXT("Disabled"),
                          bDataSharingEnabled ? TEXT("Enabled") : TEXT("Limited"));
}

// Private helper methods for config storage

bool AComprehensivePrivacyManager::GetUserConsent()
{
    // Implement your config storage retrieval here
    // Example using GameInstance or SaveGame system
    return false; // Default to no consent
}

void AComprehensivePrivacyManager::SaveUserConsent(bool bConsent)
{
    // Implement your config storage here
    // Example using GameInstance or SaveGame system
}

bool AComprehensivePrivacyManager::GetDataSharingPreference()
{
    // Implement your config storage retrieval here
    return false; // Default to limited sharing
}

void AComprehensivePrivacyManager::SaveDataSharingPreference(bool bEnabled)
{
    // Implement your config storage here
}

Best Practices:

  • Persistent Storage: Save user preferences using Unreal Engine's SaveGame system or custom config files
  • Early Initialization: Apply privacy settings during BeginPlay() before SDK initialization when possible
  • UI Sync: Keep settings UI synchronized with actual SDK state using IsAllTrackingStopped()
  • Clear Communication: Provide clear, accessible privacy controls in app settings
  • Inverse Logic: Remember that LimitDataSharing(false) means data sharing is enabled, while true means it's limited
  • Compliance Documentation: Maintain records of when and how users provide or revoke consent

Method Reference Summary

Method Description When to Use
LimitDataSharing(bool) Control third-party data sharing based on user consent CCPA compliance, user privacy preferences
TrackingOptIn() Record explicit GDPR tracking consent GDPR compliance in EU regions
StopAllTracking() Completely disable SDK tracking User declines all tracking consent
ResumeAllTracking() Re-enable tracking after stopping User changes consent to opt back in
IsAllTrackingStopped() Check current tracking status Sync UI, check state before operations
TrackingUnder13() Enable COPPA protections for children User identified as under 13 years old