Skip to content

Adjust

Herald's Adjust module sends attribution events to Adjust. Adjust only gets the events you choose: an event reaches Adjust only if it has a token from your Adjust dashboard. Purchases carry revenue, and ad impressions go through Adjust's ad-revenue API.

SDK Package
Android io.github.mkhytarmkhoian:herald-adjust
Flutter herald_adjust, over adjust_sdk
iOS herald-ios-adjust, module HeraldAdjust, over Adjust's iOS SDK
React Native @herald-analytics/adjust, over react-native-adjust

Setup

You build the AdjustConfig (ADJConfig on iOS): environment, data residency, log level, attribution callbacks. Herald's start() passes it to initSdk, so don't call initSdk yourself.

val adjust = Adjust.getDefaultInstance()
val config = AdjustConfig(application, appToken, AdjustConfig.ENVIRONMENT_PRODUCTION).apply {
    setUrlStrategy(listOf("eu.adjust.com"), true, true) // EU data residency: your setting
    setLogLevel(LogLevel.SUPPRESS)
}

val tracker = AdjustAnalyticsTrackerService(
    CompositeAdjustEventTrackerFactory(
        featureFactories + // your revenue and ad-revenue trackers, among others
            TokenAdjustEventTrackerFactory(
                tokens = listOf(
                    AdjustToken<CheckoutCompleted>("abc123"),
                    AdjustToken<PlanSelected>("def456"),
                ),
                adjust = adjust,
            ),
    ),
    CompositeAdjustPropertySetterFactory(
        GenericAdjustPropertySetterFactory(adjust),
    ),
)
val service = AdjustAnalyticsService(adjust, config) // start() calls initSdk(config)

val provider = Herald.Provider(
    name = "adjust",
    events = tracker,
    properties = tracker,
    identity = service,
    lifecycle = service,
    consent = service,
)

Adjust's iOS SDK is static functions too, so Herald's iOS classes take no Adjust object. The service takes your ADJConfig.

// Adjust returns nil for a config it can't use, such as one without an app token.
guard let config = ADJConfig(appToken: "YOUR_APP_TOKEN", environment: ADJEnvironmentProduction)
else {
    return nil
}

let tracker = AdjustAnalyticsTrackerService(
    eventTrackerFactory: CompositeAdjustEventTrackerFactory(
        featureFactories + [  // revenue and custom mappings first
            TokenAdjustEventTrackerFactory(tokens: [
                AdjustToken(CheckoutCompleted.self, "abc123")
            ])
        ]),  // nothing at the end: only tokened events are sent
    propertySetterFactory: GenericAdjustPropertySetterFactory()
)
// Starts Adjust itself, silent until consent. Don't call Adjust.initSdk yourself.
let service = AdjustAnalyticsService(config: config)

let provider = HeraldProvider(
    name: "adjust",
    events: tracker,
    properties: tracker,
    identity: service,
    lifecycle: service,
    consent: service
)

The Adjust plugin is static functions, so Herald's Flutter classes take no Adjust object.

final config = AdjustConfig('YOUR_APP_TOKEN', AdjustEnvironment.production);

final tracker = AdjustAnalyticsTrackerService(
  eventTrackerFactory: CompositeAdjustEventTrackerFactory([
    ...featureFactories, // revenue and custom mappings first
    TokenAdjustEventTrackerFactory([AdjustToken<CheckoutCompleted>('abc123')]),
  ]), // nothing at the end: only tokened events are sent
  propertySetterFactory: CompositeAdjustPropertySetterFactory([
    const GenericAdjustPropertySetterFactory(),
  ]),
);
// Initialises Adjust itself, silent until consent. Don't call Adjust.initSdk yourself.
final service = AdjustAnalyticsService(config);

final provider = HeraldProvider(
  name: 'adjust',
  events: tracker,
  properties: tracker,
  identity: service,
  lifecycle: service,
  consent: service,
);

The Adjust package is static functions too, so Herald's React Native classes take no Adjust object. The service takes your AdjustConfig.

const config = new AdjustConfig('YOUR_APP_TOKEN', AdjustConfig.EnvironmentProduction);

const tracker = new AdjustAnalyticsTrackerService({
  eventTrackerFactory: new CompositeAdjustEventTrackerFactory([
    ...featureFactories, // revenue and custom mappings first
    new TokenAdjustEventTrackerFactory([new AdjustToken(CheckoutCompleted, 'abc123')]),
  ]), // nothing at the end: only tokened events are sent
  propertySetterFactory: new CompositeAdjustPropertySetterFactory([
    new GenericAdjustPropertySetterFactory(),
  ]),
});
// Initialises Adjust itself, silent until consent. Don't call Adjust.initSdk yourself.
const service = new AdjustAnalyticsService(config);

const provider = new HeraldProvider({
  name: 'adjust',
  events: tracker,
  properties: tracker,
  identity: service,
  lifecycle: service,
  consent: service,
});

What reaches Adjust

Herald Adjust
an event with a token trackEvent(AdjustEvent(token)), with parameters as callback parameters (text)
an event without a token nothing
an AdjustRevenueEvent the same tokened event, plus setRevenue(revenue, currency) and setDeduplicationId
an AdjustAdRevenueEvent trackAdRevenue(AdjustAdRevenue(source)), no token needed
a property addGlobalCallbackParameter(name, value), sent with every later event
identify / reset a user_id global callback parameter / removing it
start disable(), then initSdk(config)
setEnabled enable() / disable()

Factories and revenue types

Factory Handles
TokenAdjustEventTrackerFactory(tokens, adjust); (tokens) on Flutter and React Native; (tokens:) on iOS events of a type that has an AdjustToken in the list
RequireMappedAdjustEventTrackerFactory nothing: throws for any event that reaches it
GenericAdjustPropertySetterFactory every property

There is no generic event factory: an Adjust event without a token doesn't exist.

Revenue types, which your Adjust factory maps your events to (see Revenue):

  • AdjustRevenueEvent: revenue, currency, and an optional deduplicationId. Send it with RevenueEventTracker(revenueEvent, token, adjust), or RevenueAdjustEventTracker(revenueEvent, token) on Flutter, iOS and React Native; the factory passes the token, so the event needs no entry in TokenAdjustEventTrackerFactory.
  • AdjustAdRevenueEvent: source, such as applovin_max_sdk; revenue; currency; and optional impression count, network, unit and placement. Send it with AdRevenueEventTracker, or AdRevenueAdjustEventTracker on Flutter, iOS and React Native.

A fresh install stays silent. start() calls disable() before initSdk(), so a disabled state is the SDK's starting state. The order matters: a disable() issued after initSdk loses the race with the first session, which then reaches Adjust's servers.

Adjust persists its enabled flag, but start() sets it to disabled on every launch. So re-apply the stored decision after start-up, as in Consent. Third-party sharing and other granular consent is set on the Adjust SDK directly.

Identity

identify writes the user id as the user_id global callback parameter; pass identityParameter to AdjustAnalyticsService to use another key. It must match a callback parameter configured in your Adjust dashboard. Don't give any property that name: it would overwrite the identity, and reset would then remove the property.

Watch out for

  • Callback parameters are text. Typed values are flattened with asString.
  • On Flutter and React Native, Herald can't see Adjust's own failures. The Adjust package doesn't wait for its native side, so an error there never reaches your error reporter.
  • On iOS, an empty token or ad revenue source is reported. Adjust itself would drop the event with only a log line, so Herald refuses it and tells your error reporter.
  • Each AdjustToken pairs a token with an event type, not a name, so the compiler checks the list and a renamed event keeps its token. An event that extends the type uses its token too:

    SDK An AdjustToken
    Android AdjustToken<SignUpCompleted>("abc123")
    Flutter AdjustToken<SignUpCompleted>('abc123')
    iOS AdjustToken(SignUpCompleted.self, "abc123")
    React Native new AdjustToken(SignUpCompleted, 'abc123')