Analytics-Kotlin Migration Guide
This guide assumes you already have an Analytics-Android Source in your Segment workspace. If you are creating a new one you can reference the Source Overview Guide
If you’re using a previous Segment mobile library such as Analytics-Android, follow these steps to migrate to the Analytics-Kotlin library. Analytics-Kotlin is designed to work with your Java codebase as well.:
1. Import Analytics-Kotlin
1.a) Add the dependencies to your app.
In your top-level build.gradle:
repositories {
mavenCentral()
}
In your app module’s build.gradle:
dependencies {
implementation 'com.segment.analytics.kotlin:android:<latest_version>'
}
You have now added Analytics-Kotlin to your project. You can remove the Analytics-Android SDK from your app.
1.b) Modify your initialized instance
val analytics = Analytics("YOUR_WRITE_KEY", context) {
trackApplicationLifecycleEvents = true
}
// Initialize an Analytics object with the Kotlin Analytics method
Analytics androidAnalytics = AndroidAnalyticsKt.Analytics("YOUR_WRITE_KEY", context, configuration -> {
configuration.setTrackApplicationLifecycleEvents(true);
return Unit.INSTANCE;
}
);
// Wrap the object with JavaAnalytics for Java Compatibility.
// You can also choose not to wrap the object, but some of the Analytics methods may not be accessible.
JavaAnalytics analytics = new JavaAnalytics(androidAnalytics);
1.c) Update your import statements
You’ll need to update the imports for Analytics-Kotlin.
Before example
import com.segment.analytics.Analytics;
import com.segment.analytics.Middleware;
After example
import com.segment.analytics.kotlin.core.Analytics;
import com.segment.analytics.kotlin.android.AndroidAnalyticsKt; // Only for calling from Android
import com.segment.analytics.kotlin.core.compat.JavaAnalytics; // Only for calling from Java
import com.segment.analytics.kotlin.core.platform.Plugin; // Replaces Middleware
Analytics-Kotlin supports running multiple instances of the analytics object, so it does not assume a singleton. However, if you’re migrating from Analytics-Android and all your track calls are routed to the Analytics.shared()
singleton, you can these calls to your new Analytics-Kotlin object.
Add this extension to your code to ensure that tracking calls written for Analytics-Android work with Analytics-Kotlin.
// Application's onCreate
...
sharedAnalytics = Analytics(...)...
fun Analytics.with {
// TODO: Finish this
return MyApplication.sharedAnalytics; // or whatever variable name you're using
}
2. Upgrade your Destinations
If your app uses Segment to route data to Destinations via Segment-cloud (i.e. Cloud-mode destinations), you can skip this step. Analytics-Kotlin treats Device-mode Destinations as plugins, and simplifies the process in integrating them into your app. Analytics-Kotlin supports these Device-Mode Destinations with more to come.
2.a) Import the Destination Plugin
implementation '<owner>:<project>:<version>'
2.b) Add Plugin to your Analytics instance:
Import the plugin:
import com.example.SomeDestinationPlugin
Add the pluging to the Analytics Instance
Your events will now begin to flow to the added destination in Device-Mode.
3. Upgrade Middleware to Plugins
Middlewares are a powerful mechanism that can augment events collected by the Analytics Android (Classic) SDK. A middleware is a simple function that is invoked by the Segment SDK and can be used to monitor, modify, augment or reject events. Analytics-Kotlin replaces the concept of middlewares with Enrichment Plugins to give you even more control over your event data. Refer to the Plugin Architecture Overview for more information.
3.a) Upgrading source middleware
Before example
builder
.useSourceMiddleware(new Middleware() {
@Override
public void intercept(Chain chain) {
// Get the payload.
BasePayload payload = chain.payload();
// Set the device year class on the context object.
int year = YearClass.get(getApplicationContext());
Map<String, Object> context = new LinkedHashMap<>(payload.context());
context.put("device_year_class", year);
// Build our new payload.
BasePayload newPayload = payload.toBuilder()
.context(context)
.build();
// Continue with the new payload.
chain.proceed(newPayload);
}
})
builder
.useSourceMiddleware(
Middleware { chain ->
// Get the payload.
val payload = chain.payload()
// Set the device year class on the context object.
val year = YearClass.get(getApplicationContext())
val context = LinkedHashMap<String, Object>(payload.context())
context.put("device_year_class", year)
// Build our new payload.
val newPayload = payload.toBuilder()
.context(context)
.build();
// Continue with the new payload.
chain.proceed(newPayload)
})
After example
analytics.add(new Plugin() {
private Analytics analytics;
@Override
public BaseEvent execute(@NonNull BaseEvent event) {
// Set the device year class on the context object.
int year = YearClass.get(getApplicationContext());
EventTransformer.putInContext(event, "device_year_class", year);
return event;
}
@Override
public void setup(@NonNull Analytics analytics) {
setAnalytics(analytics);
}
@NonNull
@Override
public Type getType() {
return Plugin.Type.Enrichment;
}
@NonNull
@Override
public Analytics getAnalytics() {
return analytics;
}
@Override
public void setAnalytics(@NonNull Analytics analytics) {
this.analytics = analytics;
}
});
analytics.add(object: Plugin {
override lateinit var analytics: Analytics
override val type = Plugin.Type.Enrichment
override fun execute(event: BaseEvent): BaseEvent? {
// Set the device year class on the context object.
val year = YearClass.get(getApplicationContext())
event.context = updateJsonObject(event.context) {
it["device_year_class"] = year
}
return event
}
})
3.b) Upgrading destination middleware
If you don’t need to transform all of your Segment calls, and only want to transform the calls going to specific, device-mode destinations, use Destination plugins.
Before example
builder
.useDestinationMiddleware("Segment.io", new Middleware() {
@Override
public void intercept(Chain chain) {
// Get the payload.
BasePayload payload = chain.payload();
// Set the device year class on the context object.
int year = YearClass.get(getApplicationContext());
Map<String, Object> context = new LinkedHashMap<>(payload.context());
context.put("device_year_class", year);
// Build our new payload.
BasePayload newPayload = payload.toBuilder()
.context(context)
.build();
// Continue with the new payload.
chain.proceed(newPayload);
}
})
builder
.useDestinationMiddleware(
"Segment.io",
Middleware { chain ->
// Get the payload.
val payload = chain.payload()
// Set the device year class on the context object.
val year = YearClass.get(getApplicationContext())
val context = LinkedHashMap<String, Object>(payload.context())
context.put("device_year_class", year)
// Build our new payload.
val newPayload = payload.toBuilder()
.context(context)
.build();
// Continue with the new payload.
chain.proceed(newPayload)
})
After example
SegmentDestination segmentDestination = analytics.find(SegmentDestination.class);
segmentDestination.add(new Plugin() {
private Analytics analytics;
@Override
public BaseEvent execute(@NonNull BaseEvent event) {
// Set the device year class on the context object.
int year = YearClass.get(getApplicationContext());
EventTransformer.putInContext(event, "device_year_class", year);
return event;
}
@Override
public void setup(@NonNull Analytics analytics) {
setAnalytics(analytics);
}
@NonNull
@Override
public Type getType() {
return Plugin.Type.Enrichment;
}
@NonNull
@Override
public Analytics getAnalytics() {
return analytics;
}
@Override
public void setAnalytics(@NonNull Analytics analytics) {
this.analytics = analytics;
}
});
val segmentDestination: DestinationPlugin = analytics.find(SegmentDestination::class)
segmentDestination.add(object: Plugin {
override lateinit var analytics: Analytics
override val type = Plugin.Type.Enrichment
override fun execute(event: BaseEvent): BaseEvent? {
// Set the device year class on the context object.
val year = YearClass.get(getApplicationContext())
event.context = updateJsonObject(event.context) {
it["device_year_class"] = year
}
return event
}
})
4. Upgrade Notes
Call Identify as a one-off after migrating to Kotlin
To preserve the userId for users identified prior to your migration to Kotlin, you must make a one-off Identify call. This is due to a storage format change between the Analytics-Android and the Analytics-Kotlin libraries.
4.a) Changes to the Configuration Object
The following option was renamed in Analytics-Kotlin:
Before | After |
---|---|
context |
Name changed to application |
defaultAPIHost |
Name changed to apiHost |
defaultProjectSettings |
Name changed to defaultSettings |
experimentalUseNewLifecycleMethods |
Name changed to useLifecycleObserver |
The following option was added in Analytics-Kotlin:
Added Option | Details |
---|---|
autoAddSegmentDestination |
The analytics client automatically adds the Segment Destination. Set this to false , if you want to customize the initialization of the Segment Destination, such as, add destination middleware). |
The following option was removed in Analytics-Kotlin:
Removed Option | Details |
---|---|
defaultOptions |
Removed in favor of a plugin that adds the default data to the event payloads. Segment doesn’t provide a plugin example since it’s dependent on your needs. |
recordScreenViews |
Removed in favor of the AndroidRecordScreenPlugin that provides the same functionality. |
trackAttributionData |
This feature no longer exists. |
4.b) Properties
Properties have been replaced by JsonElement. Since Properties are essentially a Map<String, Object>
we provide the ability to pass a map into our core tracking methods:
Map<String, Object> map = new HashMap<>();
map.put("feature", "chat");
Map<String, Object> miniMap = new HashMap<>();
miniMap.put("colorChoice", "green");
map.put("prefs", miniMap);
analytics.track("UseFeature", map);;
val map = HashMap<String, Any>()
map.put("feature", "chat")
map.put("prefs", buildJsonObject { put("colorChoice", JsonPrimitive("green")) })
analytics.track("UseFeature", map)
4.c) Options Support Removed
Options are no longer supported and should be converted into plugins.
4.d) Traits are no longer attached to analytics.track()
events automatically
To prevent sending unwanted or unnecessary PII, traits collected in analytics.identify()
events are no longer automatically attached to analytics.track()
events. To achieve this, you can write a before
plugin:
import com.segment.analytics.kotlin.core.Analytics
import com.segment.analytics.kotlin.core.Plugin
import com.segment.analytics.kotlin.core.PluginType
import com.segment.analytics.kotlin.core.platform.Plugin
import com.segment.analytics.kotlin.core.events.RawEvent
class InjectTraits : Plugin {
override val type: PluginType = PluginType.Enrichment
var analytics: Analytics? = null
override fun <T : RawEvent> execute(event: T?): T? {
if (event?.type == "identify") {
return event
}
var workingEvent = event
val context = event?.context?.toMutableMap()
if (context != null) {
context["traits"] = analytics?.traits()
workingEvent?.context = context
}
return workingEvent
}
}
Conclusion
Once you’re up and running, you can take advantage of Analytics-Kotlin’s additional features, like Destination Filters, Functions, and Typewriter support.
This page was last modified: 13 Nov 2024
Need support?
Questions? Problems? Need more info? Contact Segment Support for assistance!