Skip to content

Repository files navigation

Tenjin Unreal Engine SDK

Summary

The Unreal Engine SDK for Tenjin. To learn more about Tenjin and our product offering, please visit https://www.tenjin.com.

  • The Tenjin Unreal SDK supports both iOS and Android.
  • Review the iOS and Android documentation and apply the proper platform settings to your builds.
  • See the CHANGELOG for detailed version history.
  • For any issues or support, please contact support@tenjin.com.

Table of contents

Requirements

  • Unreal Engine 5.3 or newer
  • A C++ project (Blueprint-only projects must be converted to a code project before installing the plugin — UE compiles the plugin's C++ at package time)
  • iOS 15+ deployment target
  • Android minSdkVersion 23+, compileSdkVersion / targetSdkVersion 34
  • A Tenjin SDK key from the Tenjin dashboard

Basic integration

Get the SDK

Clone or download this repository, or add it to your project as a git submodule. Releases are tagged on GitHub.

Add the SDK to your project

  1. Copy (or git submodule add) the TenjinSDK/ folder into your project's Plugins/ directory:

    <YourProject>/Plugins/TenjinSDK/
    
  2. No manual native-dep setup needed. Both native SDKs are wired up automatically:

    • iOSTenjinSDK.xcframework is committed under Plugins/TenjinSDK/ThirdParty/iOS/ and linked via PublicAdditionalFrameworks in Build.cs. Bundled version is TenjinSDK 1.17.0. To bump, replace the framework folder per ThirdParty/iOS/README.md.
    • Android — the plugin's UPL XML injects implementation 'com.tenjin:android-sdk:1.18.0' into the generated Gradle build at package time; Gradle pulls from Maven Central during the build.
  3. Add TenjinSDK to your game module's Build.cs:

    PublicDependencyModuleNames.AddRange(new string[] {
        "Core", "CoreUObject", "Engine", "TenjinSDK"
    });
  4. Right-click your .uproject and choose Generate project files, then build the project once. The plugin compiles alongside your game module.

  5. Enable the plugin in Edit → Plugins → Analytics → Tenjin SDK. It auto-enables when listed in your .uproject.

Configure the plugin

The plugin registers a settings page at Edit → Project Settings → Plugins → Tenjin SDK:

Setting Purpose
SdkKey Your dashboard SDK key
AttUsageDescription iOS ATT prompt text (injected into Info.plist)
AndroidAppStore googleplay / amazon / other
bAutoInitialize If true, the plugin auto-initializes during module startup

Values are persisted in Config/DefaultEngine.ini:

[/Script/TenjinSDK.TenjinSettings]
SdkKey="YOUR_TENJIN_SDK_KEY"
AttUsageDescription="This identifier will be used to deliver personalized ads to you."
AndroidAppStore=GooglePlay
bAutoInitialize=False

iOS code signing

UE 5.x uses the "Modern Xcode" workflow. iOS signing is not under the iOS Runtime Settings page — it lives in Config/DefaultEngine.ini under [/Script/MacTargetPlatform.XcodeProjectSettings]:

[/Script/MacTargetPlatform.XcodeProjectSettings]
bUseAutomaticCodeSigning=True
CodeSigningTeam=XXXXXXXXXX        ; your 10-char Apple Team ID
CodeSigningPrefix=com.yourcompany ; bundle id = prefix + "." + project name

Find your Team ID in Xcode → Settings → Accounts, or decode a provisioning profile. After editing, delete Intermediate/ProjectFilesIOS/ so the generated .xcconfig is rebuilt from the new values.

Initialize and connect

The two minimum calls required to record an install.

From C++:

#include "TenjinBPLibrary.h"

UTenjinBPLibrary::Initialize(TEXT("YOUR_TENJIN_SDK_KEY"));
UTenjinBPLibrary::Connect();

From Blueprints: drag the nodes Tenjin → Initialize and Tenjin → Connect into your game's startup graph.

The relevant function signatures:

UFUNCTION(BlueprintCallable, Category = "Tenjin")
static void Initialize(const FString& SdkKey);

UFUNCTION(BlueprintCallable, Category = "Tenjin")
static void Connect();

iOS note: if your app prompts the user for App Tracking Transparency, call your ATT request before Connect() so the IDFA decision is settled before Tenjin makes its first network call.

Additional features

Once the SDK is initialized you can use any of the methods below.

Custom events

Track a named event:

UFUNCTION(BlueprintCallable, Category = "Tenjin|Events")
static void EventWithName(const FString& Name);
UTenjinBPLibrary::EventWithName(TEXT("level_complete"));

Track an event with an integer value (typed numeric is enforced — passing strings is deprecated):

UFUNCTION(BlueprintCallable, Category = "Tenjin|Events")
static void EventWithNameAndValue(const FString& Name, int32 Value);
UTenjinBPLibrary::EventWithNameAndValue(TEXT("coins_earned"), 250);

Limits: event name ≤ 80 characters, ≤ 500 unique event names per app.

Purchase events

Generic transaction (no receipt):

UFUNCTION(BlueprintCallable, Category = "Tenjin|Transactions")
static void Transaction(const FString& ProductName,
                        const FString& CurrencyCode,
                        int32 Quantity, float UnitPrice);

iOS — pass the StoreKit transaction id and base64-encoded receipt:

UFUNCTION(BlueprintCallable, Category = "Tenjin|Transactions")
static void TransactionWithReceipt(const FString& ProductName,
                                   const FString& CurrencyCode,
                                   int32 Quantity, float UnitPrice,
                                   const FString& TransactionId,
                                   const FString& Base64Receipt);

Android — pass the Play Billing purchase data JSON and signature:

UFUNCTION(BlueprintCallable, Category = "Tenjin|Transactions")
static void TransactionWithDataSignature(const FString& ProductName,
                                         const FString& CurrencyCode,
                                         int32 Quantity, float UnitPrice,
                                         const FString& PurchaseData,
                                         const FString& DataSignature);

Subscription tracking (iOS only)

UFUNCTION(BlueprintCallable, Category = "Tenjin|Transactions")
static void Subscription(const FString& ProductId,
                         const FString& CurrencyCode, float UnitPrice,
                         const FString& IosTransactionId,
                         const FString& IosOriginalTransactionId,
                         const FString& IosReceipt,
                         const FString& IosSKTransaction,
                         const FString& AndroidPurchaseToken,
                         const FString& AndroidPurchaseData,
                         const FString& AndroidDataSignature);

iOS 16+ — fetches the latest StoreKit 2 transaction natively, then sends it to Tenjin. Use this when your IAP library (e.g. RevenueCat) doesn't expose SK2 data. (Tenjin's native +subscriptionWithStoreKitForProductId: is annotated API_AVAILABLE(ios(16.0)).)

UFUNCTION(BlueprintCallable, Category = "Tenjin|Transactions")
static void SubscriptionWithStoreKit(const FString& ProductId,
                                     const FString& CurrencyCode,
                                     float UnitPrice,
                                     const FTenjinSubscriptionResultDelegate& Callback);

Android: the native Tenjin Android SDK does not yet expose a subscription API. Subscription(...) is a no-op on Android until upstream support lands.

LiveOps Campaigns (attribution)

Tenjin supports retrieving user attribution information — sourcing ad network, campaign, creative, etc. — directly from the SDK. This lets you tie attribution to in-game data per device, or show different content depending on the install source. Bind a UFUNCTION on a UObject and pass it to GetAttributionInfo (dynamic delegates can't bind to lambdas):

UFUNCTION(BlueprintCallable, Category = "Tenjin|Attribution")
static void GetAttributionInfo(const FTenjinAttributionInfoDelegate& Callback);
FTenjinAttributionInfoDelegate Cb;
Cb.BindUFunction(Listener, FName(TEXT("HandleAttributionInfo")));
UTenjinBPLibrary::GetAttributionInfo(Cb);

Json is the raw payload — ad_network, campaign_id, campaign_name, site_id, creative_name, etc. LiveOps Campaigns is a paid Tenjin feature — contact your Tenjin account manager to enable it.

Deep links

Bind the multicast delegate from any UObject (or from a Blueprint via Get Tenjin Delegates → On Deep Link Received → Bind Event), then install Tenjin's handler:

UTenjinDelegates::Get()->OnDeepLinkReceived.AddDynamic(this, &UMyClass::HandleDeepLink);
UTenjinBPLibrary::RegisterDeepLinkHandler();
UFUNCTION()
void UMyClass::HandleDeepLink(const FString& JsonPayload)
{
    // keys include clicked_tenjin_link, is_first_session,
    // deferred_deeplink_url, plus the standard attribution context.
}

User Profile — LiveOps Metrics

Tenjin automatically tracks per-user engagement metrics (session count, session length, IAP totals by currency, ad revenue by network).

UFUNCTION(BlueprintCallable, Category = "Tenjin|UserProfile")
static void GetUserProfileDictionary(const FTenjinStringDelegate& Callback);

UFUNCTION(BlueprintCallable, Category = "Tenjin|UserProfile")
static void ResetUserProfile();
FTenjinStringDelegate Cb;
Cb.BindLambda([](const FString& Json)
{
    // Parse Json for keys:
    //   session_count, total_session_time, average_session_length,
    //   last_session_length, iap_transaction_count, total_ilrd_revenue_usd,
    //   first_session_date, last_session_date, current_session_length,
    //   iap_revenue_by_currency, purchased_product_ids, ilrd_revenue_by_network
});
UTenjinBPLibrary::GetUserProfileDictionary(Cb);

Customer User ID

UFUNCTION(BlueprintCallable, Category = "Tenjin|User")
static void SetCustomerUserId(const FString& UserId);

UFUNCTION(BlueprintCallable, Category = "Tenjin|User")
static void GetCustomerUserId(const FTenjinStringDelegate& Callback);

Analytics Installation ID

A random GUID that Tenjin assigns at first launch. Useful as a stable identifier when advertising ids are unavailable.

UFUNCTION(BlueprintCallable, Category = "Tenjin|User")
static void GetAnalyticsInstallationId(const FTenjinStringDelegate& Callback);

App Subversion

Track sub-versions of your app — useful for A/B testing builds within the same store version.

UFUNCTION(BlueprintCallable, Category = "Tenjin|Config")
static void AppendAppSubversion(int32 Subversion);

Retry/cache events

UFUNCTION(BlueprintCallable, Category = "Tenjin|Config")
static void SetCacheEventSetting(bool bSetting);

UFUNCTION(BlueprintCallable, Category = "Tenjin|Config")
static void SetEncryptRequestsSetting(bool bSetting);

SetCacheEventSetting(true) enables retry-on-failure for events sent while the device is offline.

GDPR (opt-in / opt-out)

UTenjinBPLibrary::OptIn();
UTenjinBPLibrary::OptOut();
UTenjinBPLibrary::OptInParams(TArray<FString>{TEXT("country"), TEXT("device_id")});
UTenjinBPLibrary::OptOutParams(TArray<FString>{TEXT("country"), TEXT("device_id")});

Google DMA parameters

UFUNCTION(BlueprintCallable, Category = "Tenjin|Privacy")
static void SetGoogleDMAParameters(bool bAdPersonalization, bool bAdUserData);

UTenjinBPLibrary::OptInGoogleDMA();
UTenjinBPLibrary::OptOutGoogleDMA();

Opt in/out using CMP

Drive opt-in/opt-out from the IAB TCF v2 consent string emitted by your CMP:

UTenjinBPLibrary::OptInOutUsingCMP();

SKAdNetwork and Conversion Values (iOS)

iOS SKAdNetwork postback updates. Pre-iOS-16.1 SDKs ignore CoarseValue and bLockWindow.

UFUNCTION(BlueprintCallable, Category = "Tenjin|SKAdNetwork")
static void UpdatePostbackConversionValue(int32 ConversionValue);

UFUNCTION(BlueprintCallable, Category = "Tenjin|SKAdNetwork")
static void UpdatePostbackConversionValueWithCoarseValue(
    int32 ConversionValue,
    ETenjinCoarseConversionValue CoarseValue,
    bool bLockWindow);

ConversionValue is a 0–63 integer (6 bits). CoarseValue is one of Low / Medium / High / None.

Impression-Level Ad Revenue (ILRD)

ILRD passthroughs are exposed only for ad mediation platforms that ship an Unreal Engine plugin (otherwise there's no realistic way for a UE app to obtain the impression JSON). Serialise the network's impression dictionary to JSON in your app code and hand it to the matching method:

UFUNCTION(BlueprintCallable, Category = "Tenjin|ILRD")
static void EventAdImpressionAdMob(const FString& JsonPayload);

UFUNCTION(BlueprintCallable, Category = "Tenjin|ILRD")
static void EventAdImpressionAppLovin(const FString& JsonPayload);

UFUNCTION(BlueprintCallable, Category = "Tenjin|ILRD")
static void EventAdImpressionCAS(const FString& JsonPayload);

Supported networks and their UE plugins:

Network UE plugin
AppLovin MAX AppLovin/AppLovin-MAX-Unreal
Google AdMob Community / marketplace UE plugins (no official Google plugin)
CAS (Clever Ads Solutions) cleveradssolutions/CAS-Unreal

The other ILRD networks supported by Tenjin's native iOS/Android SDKs (IronSource / Unity LevelPlay, HyperBid, TopOn, TradPlus, CloudX) ship only Unity SDKs at this time, so they're intentionally omitted here. If upstream publishes a UE plugin for any of them, the corresponding method is one drop-in addition — the Tenjin native side already supports it.

ILRD is a paid Tenjin feature — please contact your Tenjin account manager before sending ILRD events in production.

App Store (Google Play / Amazon / Other)

Tenjin supports three Android app-store values: googleplay, amazon, and other (for Huawei AppGallery and other stores). The plugin defaults the project to googleplay via UTenjinSettings::AndroidAppStore. To switch at runtime:

UFUNCTION(BlueprintCallable, Category = "Tenjin|Config")
static void SetAppStore(ETenjinAppStore Type);

// ETenjinAppStore::GooglePlay | Amazon | Other

If you are publishing in an Android store other than Google Play, update the value before calling Connect() so the install is attributed to the right store.

Sample app

A working example project lives under Sample/TenjinSample/. It mounts an on-screen panel with one button per SDK method.

cd Sample/TenjinSample
open TenjinSample.uproject     # macOS — opens in Unreal Editor

Set your SDK key under Project Settings → Plugins → Tenjin SDK (or paste it into Config/DefaultEngine.ini) before tapping Initialize.

For one-shot device testing the repo ships two helper scripts:

./Scripts/test-ios.sh --deploy   # packages a signed .ipa + deploys + streams device console
./Scripts/test-android.sh        # packages, installs, launches, tails logcat

Both auto-locate Unreal under /Users/Shared/Epic Games/UE_5.* — set UE_ROOT if your install is elsewhere. Pass --no-package to skip the build step on repeat runs. See CONTRIBUTING.md for the full flag set.

Testing

You can verify your integration is working through Tenjin's Live Test Device Data Tool. Add your advertising_id (Android) or IDFA (iOS) under Support → Test Devices, then send the test events from your app. You should see live events come in on the SDK Live page.

The sample app's on-screen panel exercises every public method in UTenjinBPLibrary, so it's the fastest way to confirm Initialize + Connect + a custom event are flowing end-to-end.

Engine version support

UE version Status
5.7 Primary
5.6 Primary
5.5 Primary
5.4 Primary
5.3 Primary
5.0 – 5.2 Best-effort
4.27 Not supported in v1

The sample app's *.Target.cs files are pinned to BuildSettingsVersion.V6 / EngineIncludeOrderVersion.Unreal5_7. If you open the sample in an older engine, lower those two values to match (e.g. V4 / Unreal5_3 for UE 5.3).

Support

If you have any issues with the plugin integration or usage, please contact us at support@tenjin.com.

License

The Tenjin Unreal Engine SDK is licensed under the MIT License. See LICENSE.

About

Tenjin Unreal Engine plugin

Topics

Resources

Contributing

Stars

Watchers

Forks

Releases

Packages

Used by

Contributors

Languages