Unreal Engine SDK - Supporting SKAdNetwork & ATT

Supporting SKAdNetwork

SKAdNetwork is Apple's privacy-focused attribution framework for iOS app install campaigns. Starting with Unreal Engine SDK version 2.0.11, SKAdNetwork is enabled by default in Managed Mode, where Singular automatically updates conversion values based on your configured conversion model in the dashboard.

No Additional Configuration Required: If you're using Unreal Engine SDK version 2.0.11 or later, SKAdNetwork works out of the box. No code changes or additional settings are needed for basic functionality.

Understanding SKAN Modes

Managed Mode (Default)

In Managed Mode, Singular automatically handles conversion value updates based on the conversion model you configure in your dashboard. This is the recommended approach for most apps as it requires minimal code and provides optimal conversion tracking.

  • Automatic updates: Singular manages all conversion value updates based on user events and your configured model.
  • Dashboard configuration: Design your conversion model in the Singular dashboard without code changes.
  • Optimization: Benefit from Singular's expertise in maximizing conversion value updates within Apple's constraints.

Manual Mode (Advanced)

Manual Mode gives you complete control over conversion value updates, allowing you to implement custom logic for determining when and how to update SKAN conversion values. Use this mode only if you have specific requirements that Managed Mode cannot fulfill.

Advanced Feature: Manual Mode requires careful implementation and understanding of Apple's SKAdNetwork constraints, including conversion value update windows and limitations. Most apps should use Managed Mode.


Configuration Options

Configure SKAdNetwork behavior in the USingularSDKBPLibrary::Initialize method. The following table shows available options and their defaults.

Option Default Description
skAdNetworkEnabled True Enable SKAdNetwork support for iOS attribution tracking.
manualSKANConversionManagement False Enable Manual Mode to control conversion value updates yourself.

Note: Starting in Unreal Engine SDK version 2.0.11, SKAdNetwork is enabled by default. If you're using an older version, you need to enable SKAdNetwork by setting ode translatete="no"skAdNetworkEnabled to ode translate="="no"True or by calling SkanRegisterAppForAdNetworkAttribution().


Legacy SDK Versions

If you're using an Unreal Engine SDK version older than 2.0.11, manually enable SKAdNetwork support using the method below.

Enabling SKAdNetwork in Older SDK Versions
#

Enable via Configuration

Set skAdNetworkEnabled to True when initializing the SDK.

C++
// Initialize with SKAdNetwork enabled
bool Success = USingularSDKBPLibrary::Initialize(
    TEXT("YOUR_SDK_KEY"),
    TEXT("YOUR_SDK_SECRET"),
    60,                      // Session timeout
    TEXT(""),                // Custom user ID
    true,                    // Enable SKAdNetwork
    false,                   // Manual SKAN management
    0,                       // Wait for tracking authorization
    false,                   // OAID collection
    true,                    // Enable logging
    3,                       // Log level
    false,                   // Clipboard attribution
    TEXT(""),                // Facebook App ID
    TEXT("")                 // Custom SDID
);

if (Success)
{
    UE_LOG(LogTemp, Log, TEXT("SDK initialized with SKAdNetwork enabled"));
}

Enable via Method Call

Alternatively, call the registration method directly in your app initialization code.

C++
// Register for SKAdNetwork attribution
USingularSDKBPLibrary::SkanRegisterAppForAdNetworkAttribution();
UE_LOG(LogTemp, Log, TEXT("Registered for SKAdNetwork attribution"));

Recommendation: Upgrade to the latest Unreal Engine SDK version to benefit from automatic SKAdNetwork enablement and the latest features and bug fixes.


Configuring Manual Mode

To implement custom conversion value logic, enable Manual Mode and use the provided SDK methods to update and monitor conversion values throughout your app's lifecycle.

Using SKAdNetwork in Manual Mode (Advanced)
#

Enable Manual Mode

Set manualSKANConversionManagement to True when initializing the SDK to take control of conversion value updates.

C++
// Initialize with Manual Mode enabled
bool Success = USingularSDKBPLibrary::Initialize(
    TEXT("YOUR_SDK_KEY"),
    TEXT("YOUR_SDK_SECRET"),
    60,                      // Session timeout
    TEXT(""),                // Custom user ID
    true,                    // Enable SKAdNetwork
    true,                    // Enable Manual SKAN management
    0,                       // Wait for tracking authorization
    false,                   // OAID collection
    true,                    // Enable logging
    3,                       // Log level
    false,                   // Clipboard attribution
    TEXT(""),                // Facebook App ID
    TEXT("")                 // Custom SDID
);

if (Success)
{
    UE_LOG(LogTemp, Log, TEXT("SDK initialized with Manual SKAN Mode"));
}

Update Conversion Value

Use this method to manually update the SKAdNetwork conversion value based on your custom logic. The conversion value must be an integer between 0 and 63.

Important: This method only works when manualSKANConversionManagement is set to True. If Managed Mode is active, manual updates will be ignored.

Method Signature

C++
static bool SkanUpdateConversionValue(int conversionValue)

Usage Example

C++
// Track a sign-up event
USingularSDKBPLibrary::SendEvent(TEXT("SignUp"));

// Update the conversion value to 7
bool Success = USingularSDKBPLibrary::SkanUpdateConversionValue(7);

if (Success)
{
    UE_LOG(LogTemp, Log, TEXT("Conversion value updated to 7"));
}
else
{
    UE_LOG(LogTemp, Warning, TEXT("Failed to update conversion value"));
}

// Example: Update based on purchase tier
void OnPurchaseComplete(float PurchaseAmount)
{
    // Track revenue event
    USingularSDKBPLibrary::SendRevenue(TEXT("purchase"), TEXT("USD"), PurchaseAmount);

    // Calculate conversion value based on amount
    int ConversionValue = 10; // Default
    if (PurchaseAmount >= 100.0f)
    {
        ConversionValue = 63; // High value
    }
    else if (PurchaseAmount >= 50.0f)
    {
        ConversionValue = 40; // Medium value
    }
    else if (PurchaseAmount >= 10.0f)
    {
        ConversionValue = 20; // Low value
    }

    USingularSDKBPLibrary::SkanUpdateConversionValue(ConversionValue);
}

Get Current Conversion Value

Retrieve the current conversion value tracked by the Singular SDK. This is useful for implementing conditional logic based on the current state.

Method Signature

C++
static int SkanGetConversionValue()

Usage Example

C++
// Get the current conversion value
int CurrentValue = USingularSDKBPLibrary::SkanGetConversionValue();
UE_LOG(LogTemp, Log, TEXT("Current conversion value: %d"), CurrentValue);

// Only update if current value is below threshold
if (CurrentValue < 30)
{
    USingularSDKBPLibrary::SkanUpdateConversionValue(30);
    UE_LOG(LogTemp, Log, TEXT("Conversion value updated from %d to 30"), CurrentValue);
}

Monitor Conversion Value Updates

Set up a delegate to receive real-time notifications whenever the conversion value changes. This enables you to react to conversion value updates and log analytics or trigger other app behaviors.

Implementation Steps

  1. Add ode translate="no"o"#include "SingularDelegates.h" to the top of your class header file.
  2. Add the following delegate declaration to your class header:

    C++
    // Delegate to register for conversion value updates
    UPROPERTY(BlueprintAssignable, Category = "Singular-SDK")
    FOnConversionValueUpdated OnConversionValueUpdated;
    
    // Method that will handle the conversion value update
    UFUNCTION()
    void OnNewConversionValue(int32 ConversionValue);
  3. In your class implementation file, register the delegate:

    C++
    // Register the delegate (typically in BeginPlay or constructor)
    OnConversionValueUpdated.AddDynamic(this, &UYourClassName::OnNewConversionValue);
  4. Implement the callback method:

    C++
    ode class="="cpp"void UYourClassName::OnNewConversionValue(int32 ConversionValue)
    {
        // This method is called whenever the conversion value is updated
        UE_LOG(LogTemp, Log, TEXT("Conversion value updated to: %d"), ConversionValue);
    
        // Log the update to your analytics
        LogConversionValueUpdate(ConversionValue);
    
        // Trigger any app-specific behavior
        if (ConversionValue >= 50)
        {
            UnlockPremiumFeature();
        }
    }

Best Practice: Use the conversion value delegate to maintain a synchronized view of the current conversion state across your app. This is especially useful for debugging and ensuring your custom logic works correctly.


Additional Resources

For more comprehensive information about SKAdNetwork implementation and best practices, see the iOS SDK SKAdNetwork Implementation Guide.


Supporting App Tracking Transparency (ATT)

App Tracking Transparency (ATT) is Apple's privacy framework requiring user consent before accessing the device's IDFA (Identifier for Advertisers) and sharing user data. Implementing ATT correctly is critical for iOS attribution and maximizing the accuracy of your user acquisition campaigns.

Why ATT Matters for Attribution

Starting with iOS 14.5, apps must request user permission through the ATT framework before accessing the IDFA. While attribution is still possible without the IDFA using fingerprinting and probabilistic methods, having the IDFA significantly improves attribution accuracy and provides deterministic matching.

  • Deterministic attribution: The IDFA enables precise, device-level attribution that connects ad impressions directly to installs.
  • Ad network optimization: Ad networks can better optimize campaigns and provide more accurate reporting with IDFA access.
  • User-level insights: Access to IDFA allows for more granular user behavior analysis and cohort tracking.

Recommendation: Singular strongly recommends implementing the ATT prompt and requesting user consent. Explain the benefits to users (personalized ads, better app experience) to maximize opt-in rates.


SDK Initialization Timing

The timing of SDK initialization relative to the ATT prompt is critical. If the SDK initializes and sends the first session before the user responds to the ATT prompt, the IDFA won't be captured, resulting in less accurate attribution.

Why Delay Initialization?

By default, the Singular SDK sends a session immediately upon initialization. When this session comes from a new device, it triggers Singular's attribution process using only the data available at that moment. If the user hasn't yet responded to the ATT prompt, the IDFA will be unavailable, and the attribution will be based on less accurate methods.

Critical: Always request ATT consent and retrieve the IDFA before the Singular SDK sends its first session. Failing to do so will permanently lose the IDFA for that device's attribution data.


Configure SDK Wait Timeout

Configure the SDK to wait for the user's ATT response before initializing by setting the waitForTrackingAuthorizationWithTimeoutInterval parameter. This delay gives the user time to respond to the ATT prompt while preventing indefinite delays if the prompt is not shown.

How ATT Delay Works

  1. The SDK initializes and starts recording a session and user events but does not send them to Singular servers yet.
  2. As soon as App Tracking Transparency consent is granted, denied, or the timeout elapses, the SDK sends the session and queued events to Singular (with or without IDFA).
  3. Singular starts the attribution process, taking advantage of the IDFA if available.

Implementation

Configuration Example

Set the waitForTrackingAuthorizationWithTimeoutInterval parameter when initializing the SDK. This value represents the maximum number of seconds to wait for the user's ATT response.

C++
// Initialize with ATT delay
bool Success = USingularSDKBPLibrary::Initialize(
    TEXT("YOUR_SDK_KEY"),
    TEXT("YOUR_SDK_SECRET"),
    60,                      // Session timeout
    TEXT(""),                // Custom user ID
    true,                    // Enable SKAdNetwork
    false,                   // Manual SKAN management
    300,                     // Wait up to 300 seconds (5 minutes) for ATT response
    false,                   // OAID collection
    true,                    // Enable logging
    3,                       // Log level
    false,                   // Clipboard attribution
    TEXT(""),                // Facebook App ID
    TEXT("")                 // Custom SDID
);

if (Success)
{
    UE_LOG(LogTemp, Log, TEXT("SDK initialized with ATT delay"));
}

Timeout Consideration: Set an appropriate timeout value (typically 60-300 seconds). If the timeout elapses before the user responds, the SDK proceeds to send data without the IDFA.


ATT Best Practices

  • Pre-prompt messaging: Show users a pre-ATT screen explaining why you need tracking permission and how it benefits them (better ads, improved experience). This can significantly increase opt-in rates.
  • Timing matters: Show the ATT prompt at a natural moment in your app flow, not immediately on first launch. Let users experience your app first.
  • Custom messaging: Customize your ATT prompt message in your Info.plist file using the NSUserTrackingUsageDescription key to clearly explain your tracking purpose.
  • Test thoroughly: Test both authorized and denied scenarios to ensure your app functions correctly regardless of the user's choice.
  • Respect user choice: Never repeatedly prompt users who have denied tracking or show aggressive messaging that pressures them to opt in.

App Store Review: Apps that don't properly implement ATT or attempt to circumvent the framework may be rejected during App Store review. Ensure your implementation follows Apple's guidelines and respects user privacy choices.


Additional Resources

For more information about iOS 14+ changes and best practices, see the following resources: